diff --git a/src/apps/backup/rubrik/restapi/custom/api.pm b/src/apps/backup/rubrik/restapi/custom/api.pm index f180378ec4..56b0590d23 100644 --- a/src/apps/backup/rubrik/restapi/custom/api.pm +++ b/src/apps/backup/rubrik/restapi/custom/api.pm @@ -1,5 +1,5 @@ # -# Copyright 2024 Centreon (http://www.centreon.com/) +# Copyright 2026-Present Centreon (http://www.centreon.com/) # # Centreon is a full-fledged industry-strength solution that meets # the needs in IT infrastructure and application monitoring for @@ -24,39 +24,38 @@ use strict; use warnings; use centreon::plugins::http; use centreon::plugins::statefile; -use JSON::XS; -use Digest::MD5 qw(md5_hex); +use centreon::plugins::misc qw/json_encode json_decode/; +use Digest::SHA qw(sha256_hex); sub new { my ($class, %options) = @_; my $self = {}; bless $self, $class; - if (!defined($options{output})) { + unless ($options{output}) { print "Class Custom: Need to specify 'output' argument.\n"; exit 3; } - if (!defined($options{options})) { - $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); - $options{output}->option_exit(); - } + $options{output}->option_exit(short_msg => "Class Custom: Need to specify 'options' argument.") + unless $options{options}; - if (!defined($options{noptions})) { + unless ($options{noptions}) { $options{options}->add_options(arguments => { - 'api-username:s' => { name => 'api_username' }, - 'api-password:s' => { name => 'api_password' }, - 'service-account:s' => { name => 'service_account' }, - 'secret:s' => { name => 'secret' }, - 'organization-id:s' => { name => 'organization_id' }, - 'hostname:s' => { name => 'hostname' }, - 'port:s' => { name => 'port' }, - 'proto:s' => { name => 'proto' }, - 'timeout:s' => { name => 'timeout' }, - 'unknown-http-status:s' => { name => 'unknown_http_status' }, - 'warning-http-status:s' => { name => 'warning_http_status' }, - 'critical-http-status:s' => { name => 'critical_http_status' }, + 'api-username:s' => { name => 'api_username', default => '' }, + 'api-password:s' => { name => 'api_password', default => '' }, + 'service-account:s' => { name => 'service_account', default => '' }, + 'secret:s' => { name => 'secret', default => '' }, + 'organization-id:s' => { name => 'organization_id', default => '' }, + 'hostname:s' => { name => 'hostname', default => '' }, + 'port:s' => { name => 'port', default => 443 }, + 'proto:s' => { name => 'proto', default => 'https' }, + 'timeout:s' => { name => 'timeout', default => 30 }, + 'unknown-http-status:s' => { name => 'unknown_http_status', default => '%{http_code} < 200 or %{http_code} >= 300' }, + 'warning-http-status:s' => { name => 'warning_http_status', default => '' }, + 'critical-http-status:s' => { name => 'critical_http_status', default => '' }, 'token:s' => { name => 'token' }, - 'cache-use' => { name => 'cache_use' } + 'cache-use' => { name => 'cache_use' }, + 'use-cdm-authent' => { name => 'use_cdm_authent' } }); } $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); @@ -80,47 +79,32 @@ sub set_defaults {} sub check_options { my ($self, %options) = @_; - $self->{option_results}->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; - $self->{option_results}->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; - $self->{option_results}->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 30; - $self->{api_username} = (defined($self->{option_results}->{api_username})) ? $self->{option_results}->{api_username} : ''; - $self->{api_password} = (defined($self->{option_results}->{api_password})) ? $self->{option_results}->{api_password} : ''; - $self->{service_account} = (defined($self->{option_results}->{service_account})) ? $self->{option_results}->{service_account} : ''; - $self->{secret} = (defined($self->{option_results}->{secret})) ? $self->{option_results}->{secret} : ''; - $self->{organization_id} = (defined($self->{option_results}->{organization_id})) ? $self->{option_results}->{organization_id} : ''; - $self->{unknown_http_status} = (defined($self->{option_results}->{unknown_http_status})) ? $self->{option_results}->{unknown_http_status} : '%{http_code} < 200 or %{http_code} >= 300'; - $self->{warning_http_status} = (defined($self->{option_results}->{warning_http_status})) ? $self->{option_results}->{warning_http_status} : ''; - $self->{critical_http_status} = (defined($self->{option_results}->{critical_http_status})) ? $self->{option_results}->{critical_http_status} : ''; - $self->{token} = $self->{option_results}->{token}; + $self->{$_} = $self->{option_results}->{$_} + foreach qw/api_username api_password service_account secret organization_id unknown_http_status warning_http_status critical_http_status token use_cdm_authent/; $self->{cache}->check_options(option_results => $self->{option_results}); - if (!defined($self->{option_results}->{hostname}) || $self->{option_results}->{hostname} eq '') { - $self->{output}->add_option_msg(short_msg => 'Need to specify --hostname option.'); - $self->{output}->option_exit(); - } + $self->{output}->option_exit(short_msg => 'Need to specify --hostname option.') + if $self->{option_results}->{hostname} eq ''; + if (defined($self->{token})) { $self->{cache_connect}->check_options(option_results => $self->{option_results}); - return 0 if ($self->{token} ne ''); + return 0 if $self->{token} ne ''; } if ($self->{service_account} ne '') { - if ($self->{secret} eq '') { - $self->{output}->add_option_msg(short_msg => 'Need to specify --secret option.'); - $self->{output}->option_exit(); - } + $self->{output}->option_exit(short_msg => 'Need to specify --secret option.') + if $self->{secret} eq ''; + $self->{cache_connect}->check_options(option_results => $self->{option_results}); return 0; } - if ($self->{api_username} eq '') { - $self->{output}->add_option_msg(short_msg => 'Need to specify --api-username option.'); - $self->{output}->option_exit(); - } - if ($self->{api_password} eq '') { - $self->{output}->add_option_msg(short_msg => 'Need to specify --api-password option.'); - $self->{output}->option_exit(); - } + $self->{output}->option_exit(short_msg => 'Need to specify either --service-account or --api-username/--api-password or --token option.') + if $self->{api_username} eq ''; + + $self->{output}->option_exit(short_msg => 'Need to specify --api-password option.') + if $self->{api_password} eq ''; return 0; } @@ -128,9 +112,14 @@ sub check_options { sub settings { my ($self, %options) = @_; - return if (defined($self->{settings_done})); $self->{http}->add_header(key => 'Accept', value => 'application/json'); - $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); + unless($options{skip_content_type}) { + $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); + } else { + $self->{http}->remove_header(key => 'Content-Type'); + } + + return if (defined($self->{settings_done})); $self->{http}->set_options(%{$self->{option_results}}); $self->{settings_done} = 1; } @@ -144,15 +133,16 @@ sub get_connection_info { sub get_token { my ($self, %options) = @_; - my $has_cache_file = $self->{cache_connect}->read(statefile => 'rubrik_api_' . md5_hex($self->{option_results}->{hostname} . '_' . $self->{api_username})); - my $token = $self->{cache_connect}->get(name => 'token'); - my $md5_secret_cache = $self->{cache_connect}->get(name => 'md5_secret'); - my $md5_secret = md5_hex($self->{api_username} . $self->{api_password}); + my $has_cache_file = $self->{cache_connect}->read(statefile => 'rubrik_api_' . sha256_hex($self->{option_results}->{hostname} . '_' . $self->{api_username})); + my $token = $self->{cache_connect}->get(name => 'token', default => ''); + my $sha_secret_cache = $self->{cache_connect}->get(name => 'sha_secret', default => ''); + my $sha_secret = sha256_hex($self->{api_username} .'##'. $self->{api_password}); if ($has_cache_file == 0 || - !defined($token) || - (defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret) + $token eq '' || + $sha_secret_cache ne $sha_secret ) { + $self->settings(); my $content = $self->{http}->request( method => 'POST', @@ -166,20 +156,13 @@ sub get_token { critical_status => $self->{critical_http_status} ); - my $decoded; - eval { - $decoded = JSON::XS->new->utf8->decode($content); - }; - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); - $self->{output}->option_exit(); - } + my $decoded = json_decode($content, output => $self->{output}); $token = $decoded->{token}; my $datas = { updated => time(), token => $decoded->{token}, - md5_secret => $md5_secret + sha_secret => $sha_secret }; $self->{cache_connect}->write(data => $datas); } @@ -187,17 +170,17 @@ sub get_token { return $token; } -sub get_service_account_token { +sub get_deprecated_service_account_token { my ($self, %options) = @_; - my $has_cache_file = $self->{cache_connect}->read(statefile => 'rubrik_api_' . md5_hex($self->{option_results}->{hostname} . '_' . $self->{service_account})); - my $token = $self->{cache_connect}->get(name => 'token'); - my $md5_secret_cache = $self->{cache_connect}->get(name => 'md5_secret'); - my $md5_secret = md5_hex($self->{service_account} . $self->{secret}); + my $has_cache_file = $self->{cache_connect}->read(statefile => 'rubrik_api_' . sha256_hex($self->{option_results}->{hostname} . '_' . $self->{service_account})); + my $token = $self->{cache_connect}->get(name => 'token', default => ''); + my $sha_secret_cache = $self->{cache_connect}->get(name => 'sha_secret', default => ''); + my $sha_secret = sha256_hex($self->{service_account} . '##'. $self->{secret}); if ($has_cache_file == 0 || - !defined($token) || - (defined($md5_secret_cache) && $md5_secret_cache ne $md5_secret) + $token eq '' || + $sha_secret_cache ne $sha_secret ) { my $json_request = { serviceAccountId => $self->{service_account}, @@ -205,16 +188,9 @@ sub get_service_account_token { }; $json_request->{organizationId} = $self->{organization_id} if ($self->{organization_id} ne ''); - my $encoded; - eval { - $encoded = encode_json($json_request); - }; - if ($@) { - $self->{output}->add_option_msg(short_msg => 'cannot encode json request'); - $self->{output}->option_exit(); - } + my $encoded = json_encode($json_request, output => $self->{output}); - $self->settings(); + $self->settings(content_type => 'application/x-www-form-urlencoded'); my $content = $self->{http}->request( method => 'POST', url_path => '/api/v1/service_account/session', @@ -224,20 +200,54 @@ sub get_service_account_token { critical_status => $self->{critical_http_status} ); - my $decoded; - eval { - $decoded = JSON::XS->new->utf8->decode($content); - }; - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response"); - $self->{output}->option_exit(); - } + my $decoded = json_decode($content, output => $self->{output}); $token = $decoded->{token}; my $datas = { updated => time(), token => $decoded->{token}, - md5_secret => $md5_secret + sha_secret => $sha_secret + }; + $self->{cache_connect}->write(data => $datas); + } + + return $token; +} + +sub get_rsc_token { + my ($self, %options) = @_; + + my $has_cache_file = $self->{cache_connect}->read(statefile => 'rubrik_api_' . sha256_hex($self->{option_results}->{hostname} . '_' . $self->{service_account})); + my $token = $self->{cache_connect}->get(name => 'access_token', default => ''); + my $expires_at = $self->{cache_connect}->get(name => 'expires_at'); + my $sha_secret_cache = $self->{cache_connect}->get(name => 'sha_secret', default => ''); + my $sha_secret = sha256_hex($self->{service_account} . $self->{secret}); + + if ($has_cache_file == 0 || + $token eq '' || + (defined($expires_at) && $expires_at < time() + 60) || + $sha_secret_cache ne $sha_secret + ) { + + $self->settings(skip_content_type => 1); + my $content = $self->{http}->request( + method => 'POST', + url_path => '/api/client_token', + post_params => { 'client_id' => $self->{service_account}, 'client_secret' => $self->{secret}, 'grant_type' => 'client_credentials' }, + unknown_status => $self->{unknown_http_status}, + warning_status => $self->{warning_http_status}, + critical_status => $self->{critical_http_status} + ); + + my $decoded = json_decode($content, output => $self->{output}); + + $token = $decoded->{access_token}; + my $expires_in = $decoded->{expires_in} // 43200; # 12h by default + my $datas = { + updated => time(), + access_token => $token, + expires_at => time() + $expires_in, + sha_secret => $sha_secret }; $self->{cache_connect}->write(data => $datas); } @@ -255,21 +265,27 @@ sub clean_token { sub credentials { my ($self, %options) = @_; - my $token = $self->{token}; - if (defined($self->{token}) && $self->{token} eq '') { - $token = $self->get_token(); - } + my $token; my $creds = {}; if ($self->{service_account} ne '') { - $token = $self->get_service_account_token(); + unless ($self->{use_cdm_authent}) { + $token = $self->get_rsc_token(); + } else { + # No longer supported since Rubrik 9.4 + $token = $self->get_deprecated_service_account_token(); + } $creds = { header => ['Authorization: Bearer ' . $token], unknown_status => '', warning_status => '', critical_status => '' }; - } elsif (defined($self->{token})) { + } elsif (defined $self->{token}) { + $token = $self->{token}; + $token = $self->get_token() + if $token eq ''; + $creds = { header => ['Authorization: Bearer ' . $token], unknown_status => '', @@ -322,14 +338,7 @@ sub request_api_paginate { return } - my $decoded; - eval { - $decoded = JSON::XS->new->allow_nonref(1)->utf8->decode($content); - }; - if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)"); - $self->{output}->option_exit(); - } + my $decoded = json_decode($content, allow_nonref => 1, output => $self->{output}); return $decoded if (ref($decoded) ne 'HASH'); @@ -365,7 +374,7 @@ sub request_api { ); # Maybe token is invalid. so we retry - if (defined($self->{token}) && $self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + if (((defined $self->{token}) || $self->{service_account} ne '') && ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300)) { $self->clean_token(); $creds = $self->credentials(); $creds->{unknown_status} = $self->{unknown_http_status}; @@ -391,7 +400,7 @@ sub request_api { sub write_cache_file { my ($self, %options) = @_; - $self->{cache}->read(statefile => 'cache_rubrik_' . $options{statefile} . '_' . md5_hex($self->get_connection_info())); + $self->{cache}->read(statefile => 'cache_rubrik_' . $options{statefile} . '_' . sha256_hex($self->get_connection_info())); $self->{cache}->write(data => { update_time => time(), response => $options{response} @@ -401,7 +410,7 @@ sub write_cache_file { sub get_cache_file_response { my ($self, %options) = @_; - $self->{cache}->read(statefile => 'cache_rubrik_' . $options{statefile} . '_' . md5_hex($self->get_connection_info())); + $self->{cache}->read(statefile => 'cache_rubrik_' . $options{statefile} . '_' . sha256_hex($self->get_connection_info())); my $response = $self->{cache}->get(name => 'response'); if (!defined($response)) { $self->{output}->add_option_msg(short_msg => 'Cache file missing'); @@ -465,10 +474,17 @@ Specify https if needed (default: 'https') =item B<--service-account> Service account ID (with --secret and --organization-id options). +For C (Rubrik Security Cloud) client credentials, use the client_id (format: client|...). =item B<--secret> Service account secret (with --service-account and --organization-id options). +For C, use the client_secret. + +=item B<--use-cdm-authent> + +Use C authentication for service account. This method is no longer supported since Rubrik 9.4. +If option is set, token is created with the old API endpoint (/api/v1/service_account/session) instead of the new one (/api/client_token). =item B<--organization-id> @@ -485,6 +501,7 @@ API password. =item B<--token> Use token authentication. If option is empty, token is created. +This method is no longer supported since Rubrik 9.4. =item B<--timeout> diff --git a/src/centreon/plugins/backend/http/curl.pm b/src/centreon/plugins/backend/http/curl.pm index 9697251ce1..6107965b50 100644 --- a/src/centreon/plugins/backend/http/curl.pm +++ b/src/centreon/plugins/backend/http/curl.pm @@ -1,5 +1,5 @@ # -# Copyright 2024 Centreon (http://www.centreon.com/) +# Copyright 2026-Present Centreon (http://www.centreon.com/) # # Centreon is a full-fledged industry-strength solution that meets # the needs in IT infrastructure and application monitoring for @@ -187,7 +187,11 @@ sub set_method { } if ($options{request}->{method} eq 'POST') { - $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1); + if ((defined $options{request}->{post_params}) || (defined $options{request}->{query_form_post})) { + $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1); + } else { + $self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => 'POST'); + } $self->{curl_log}->log('-X', $options{request}->{method}) unless $skip_log_post; return; } diff --git a/src/centreon/plugins/misc.pm b/src/centreon/plugins/misc.pm index 951a4f7804..4debd5ec0d 100644 --- a/src/centreon/plugins/misc.pm +++ b/src/centreon/plugins/misc.pm @@ -858,7 +858,7 @@ sub json_decode { } } - my $object = eval { $decoder->decode($content) }; + my $object = eval { $decoder->allow_nonref($options{allow_nonref} // 0)->decode($content) }; if ($@) { # To keep compatibilty with old json_decode: @@ -1536,6 +1536,8 @@ JSON:::PP::Boolean values. C<1> => strings, C<0> => booleans. =item * C - Do not print error on STDERR if C is not defined. +=item * C - Allow JSON string to be a non-reference value (a string, a number ...). By default, JSON string must be an array or an object. + =back =back diff --git a/src/centreon/plugins/statefile.pm b/src/centreon/plugins/statefile.pm index d0365c4f4b..64e3a011cc 100644 --- a/src/centreon/plugins/statefile.pm +++ b/src/centreon/plugins/statefile.pm @@ -356,6 +356,7 @@ sub get { if (defined($self->{datas}->{ $options{name} })) { return $self->{datas}->{ $options{name} }; } + return $options{default} if exists $options{default}; return undef; } @@ -571,6 +572,8 @@ Retrieves a value from the state file data. =item * C - The key name of the value to retrieve. +=item * C - Default value to return if value does not exist or is undef. + =back =back diff --git a/tests/apps/backup/rubrik/restapi/applications-rubrik-restapi.json b/tests/apps/backup/rubrik/restapi/applications-rubrik-restapi.json index 9b155d3f82..cb0c3823fd 100644 --- a/tests/apps/backup/rubrik/restapi/applications-rubrik-restapi.json +++ b/tests/apps/backup/rubrik/restapi/applications-rubrik-restapi.json @@ -1,92 +1,259 @@ { - "uuid": "c8fba1bd-af32-4acd-83a2-5b26a0152183", - "lastMigration": 32, - "name": "Rubrik2", - "endpointPrefix": "api/v1", - "latency": 0, - "port": 3000, - "hostname": "", - "folders": [], - "routes": [ + "uuid": "c8fba1bd-af32-4acd-83a2-5b26a0152183", + "lastMigration": 33, + "name": "Rubrik2", + "endpointPrefix": "", + "latency": 0, + "port": 3333, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "b9fdb329-671b-48d0-a906-d58e45a70527", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/job_monitoring", + "responses": [ { - "uuid": "b9fdb329-671b-48d0-a906-d58e45a70527", - "type": "http", - "documentation": "", - "method": "get", - "endpoint": "job_monitoring", - "responses": [ - { - "uuid": "4aac7b2c-2947-4e6b-83a4-094da73ecc5a", - "body": "{\r\n \"jobMonitoringInfoList\": [\r\n {\r\n \"jobMonitoringState\": \"Success\",\r\n \"jobStatus\": \"Success\",\r\n \"jobType\": \"Backup\",\r\n \"objectId\": \"VolumeGroup:::123a456-123a-456b-789c-123456789\",\r\n \"objectType\": \"WindowsVolumeGroup\",\r\n \"objectName\": \"centreon.groupe.active volumes\",\r\n \"locationId\": \"Host:::123c456-a123-b456-c789-123a456\",\r\n \"locationName\": \"centreon.groupe.active\",\r\n \"slaDomainId\": \"1234a456-123a-456b-aaaa-12345\",\r\n \"slaDomainName\": \"CENTREON-DOMAIN\",\r\n \"startTime\": \"2024-07-18T20:00:01.382Z\",\r\n \"endTime\": \"2024-07-19T06:42:16.355Z\",\r\n \"lastSuccessfulJobTime\": \"2024-07-16T20:00:01.382Z\",\r\n \"nextJobTime\": \"2023-11-07T20:00:00.000Z\",\r\n \"isFirstFullSnapshot\": false,\r\n \"sourceClusterName\": \"LOCAL\",\r\n \"sourceClusterId\": \"aaaaaa-123a-456b-aaaa-12345\",\r\n \"retryCount\": 0,\r\n \"maximumAttemptsForJob\": 3,\r\n \"dataTransferred\": 571231961088,\r\n \"objectLogicalSize\": 193955119497216,\r\n \"eventSeriesId\": \"Zaaaaaa-123a-456b-123a-12345abcd\",\r\n \"duration\": 38534973,\r\n \"nodeId\": \"AAAABBBBCCCCDDD\",\r\n \"warningCount\": 0,\r\n \"lastUpdatedTime\": \"2024-07-19T06:42:16.721Z\",\r\n \"isLogTask\": false,\r\n \"isOnDemand\": false,\r\n \"retryStatus\": \"NotRetried\"\r\n },\r\n {\r\n \"jobMonitoringState\": \"Scheduled\",\r\n \"jobStatus\": \"Scheduled\",\r\n \"jobType\": \"Backup\",\r\n \"objectId\": \"VolumeGroup:::123a456-123a-456b-789c-123456789\",\r\n \"objectType\": \"WindowsVolumeGroup\",\r\n \"objectName\": \"centreon.groupe.active volumes\",\r\n \"locationId\": \"Host:::123c456-a123-b456-c789-123a456\",\r\n \"locationName\": \"centreon.groupe.active\",\r\n \"slaDomainId\": \"1234a456-123a-456b-aaaa-12345\",\r\n \"slaDomainName\": \"CENTREON-DOMAIN\",\r\n \"startTime\": \"2024-07-18T20:00:00.000Z\",\r\n \"lastSuccessfulJobTime\": \"2024-07-18T20:00:01.382Z\",\r\n \"isFirstFullSnapshot\": false,\r\n \"sourceClusterName\": \"LOCAL\",\r\n \"sourceClusterId\": \"aaaaaa-123a-456b-aaaa-12345\",\r\n \"retryCount\": 0,\r\n \"maximumAttemptsForJob\": 3,\r\n \"objectLogicalSize\": 193955119497216,\r\n \"eventSeriesId\": \"afba57d8-efb3-452e-892b-157acc6b6069\",\r\n \"nodeId\": \"AAAABBBBCCCCDDD\",\r\n \"warningCount\": 0,\r\n \"lastUpdatedTime\": \"2024-07-18T06:42:16.894Z\",\r\n \"isLogTask\": false,\r\n \"isOnDemand\": false,\r\n \"retryStatus\": \"NotRetried\"\r\n }\r\n ],\r\n \"jobType\": \"Backup\",\r\n \"shouldIncludeLogRelatedJob\": false,\r\n \"objectName\": \"CENTREON\",\r\n \"hasMore\": false\r\n}", - "latency": 0, - "statusCode": 200, - "label": "", - "headers": [], - "bodyType": "INLINE", - "filePath": "", - "databucketID": "", - "sendFileAsBody": false, - "rules": [], - "rulesOperator": "OR", - "disableTemplating": false, - "fallbackTo404": false, - "default": true, - "crudKey": "id", - "callbacks": [] - } - ], - "responseMode": null + "uuid": "4aac7b2c-2947-4e6b-83a4-094da73ecc5a", + "body": "{\r\n \"jobMonitoringInfoList\": [\r\n {\r\n \"jobMonitoringState\": \"Success\",\r\n \"jobStatus\": \"Success\",\r\n \"jobType\": \"Backup\",\r\n \"objectId\": \"VolumeGroup:::123a456-123a-456b-789c-123456789\",\r\n \"objectType\": \"WindowsVolumeGroup\",\r\n \"objectName\": \"centreon.groupe.active volumes\",\r\n \"locationId\": \"Host:::123c456-a123-b456-c789-123a456\",\r\n \"locationName\": \"centreon.groupe.active\",\r\n \"slaDomainId\": \"1234a456-123a-456b-aaaa-12345\",\r\n \"slaDomainName\": \"CENTREON-DOMAIN\",\r\n \"startTime\": \"2024-07-18T20:00:01.382Z\",\r\n \"endTime\": \"2024-07-19T06:42:16.355Z\",\r\n \"lastSuccessfulJobTime\": \"2024-07-16T20:00:01.382Z\",\r\n \"nextJobTime\": \"2023-11-07T20:00:00.000Z\",\r\n \"isFirstFullSnapshot\": false,\r\n \"sourceClusterName\": \"LOCAL\",\r\n \"sourceClusterId\": \"aaaaaa-123a-456b-aaaa-12345\",\r\n \"retryCount\": 0,\r\n \"maximumAttemptsForJob\": 3,\r\n \"dataTransferred\": 571231961088,\r\n \"objectLogicalSize\": 193955119497216,\r\n \"eventSeriesId\": \"Zaaaaaa-123a-456b-123a-12345abcd\",\r\n \"duration\": 38534973,\r\n \"nodeId\": \"AAAABBBBCCCCDDD\",\r\n \"warningCount\": 0,\r\n \"lastUpdatedTime\": \"2024-07-19T06:42:16.721Z\",\r\n \"isLogTask\": false,\r\n \"isOnDemand\": false,\r\n \"retryStatus\": \"NotRetried\"\r\n },\r\n {\r\n \"jobMonitoringState\": \"Scheduled\",\r\n \"jobStatus\": \"Scheduled\",\r\n \"jobType\": \"Backup\",\r\n \"objectId\": \"VolumeGroup:::123a456-123a-456b-789c-123456789\",\r\n \"objectType\": \"WindowsVolumeGroup\",\r\n \"objectName\": \"centreon.groupe.active volumes\",\r\n \"locationId\": \"Host:::123c456-a123-b456-c789-123a456\",\r\n \"locationName\": \"centreon.groupe.active\",\r\n \"slaDomainId\": \"1234a456-123a-456b-aaaa-12345\",\r\n \"slaDomainName\": \"CENTREON-DOMAIN\",\r\n \"startTime\": \"2024-07-18T20:00:00.000Z\",\r\n \"lastSuccessfulJobTime\": \"2024-07-18T20:00:01.382Z\",\r\n \"isFirstFullSnapshot\": false,\r\n \"sourceClusterName\": \"LOCAL\",\r\n \"sourceClusterId\": \"aaaaaa-123a-456b-aaaa-12345\",\r\n \"retryCount\": 0,\r\n \"maximumAttemptsForJob\": 3,\r\n \"objectLogicalSize\": 193955119497216,\r\n \"eventSeriesId\": \"afba57d8-efb3-452e-892b-157acc6b6069\",\r\n \"nodeId\": \"AAAABBBBCCCCDDD\",\r\n \"warningCount\": 0,\r\n \"lastUpdatedTime\": \"2024-07-18T06:42:16.894Z\",\r\n \"isLogTask\": false,\r\n \"isOnDemand\": false,\r\n \"retryStatus\": \"NotRetried\"\r\n }\r\n ],\r\n \"jobType\": \"Backup\",\r\n \"shouldIncludeLogRelatedJob\": false,\r\n \"objectName\": \"CENTREON\",\r\n \"hasMore\": false\r\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] } - ], - "rootChildren": [ + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "86cd3393-7cfe-4964-bbd4-5bcb0dd821a1", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/v1/service_account/session", + "responses": [ { - "type": "route", - "uuid": "b9fdb329-671b-48d0-a906-d58e45a70527" + "uuid": "2850279d-a8ee-4b21-bf2c-e687a473ff26", + "body": "{\n \"token\": \"R@X@R\"\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] } - ], - "proxyMode": false, - "proxyHost": "", - "proxyRemovePrefix": false, - "tlsOptions": { - "enabled": false, - "type": "CERT", - "pfxPath": "", - "certPath": "", - "keyPath": "", - "caPath": "", - "passphrase": "" + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 }, - "cors": true, - "headers": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Access-Control-Allow-Origin", - "value": "*" - }, - { - "key": "Access-Control-Allow-Methods", - "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" - }, + { + "uuid": "40ed6cca-9aac-4fa0-8d86-84b1e487121f", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/internal/cluster/me/disk", + "responses": [ { - "key": "Access-Control-Allow-Headers", - "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + "uuid": "964bdf4a-6bbb-413f-8c54-39b00ed7a403", + "body": "{\n \"totalCapacity\": 1000000000000,\n \"usedCapacity\": 600000000000,\n \"availableCapacity\": 400000000000\n}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] } - ], - "proxyReqHeaders": [ + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "70d068f9-ab60-4019-965c-dc4465633019", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/client_token", + "responses": [ { - "key": "", - "value": "" + "uuid": "31f4465c-3ce7-43cb-b45a-bb25f148f8dd", + "body": "{\"client_id\":\"client|c9bba9a9-1234-1234-b7c6-123440b4cf64\",\"access_token\":\"S3cR3t#t@k3n\",\"expires_in\":43200}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] } - ], - "proxyResHeaders": [ + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "eb98e414-7262-42c6-ae76-4fd99c556385", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/internal/cluster/me/name", + "responses": [ { - "key": "", - "value": "" + "uuid": "56e96b5b-832e-4b2a-af75-fc080f9eb9b3", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [], + "body": "\"toto\"" } - ], - "data": [], - "callbacks": [] + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "b9fdb329-671b-48d0-a906-d58e45a70527" + }, + { + "type": "route", + "uuid": "86cd3393-7cfe-4964-bbd4-5bcb0dd821a1" + }, + { + "type": "route", + "uuid": "40ed6cca-9aac-4fa0-8d86-84b1e487121f" + }, + { + "type": "route", + "uuid": "70d068f9-ab60-4019-965c-dc4465633019" + }, + { + "type": "route", + "uuid": "eb98e414-7262-42c6-ae76-4fd99c556385" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [], + "callbacks": [] } \ No newline at end of file diff --git a/tests/apps/backup/rubrik/restapi/authent.robot b/tests/apps/backup/rubrik/restapi/authent.robot new file mode 100644 index 0000000000..c03e5d9c01 --- /dev/null +++ b/tests/apps/backup/rubrik/restapi/authent.robot @@ -0,0 +1,34 @@ +*** Settings *** +Documentation Check Rubrik REST API authentication + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}applications-rubrik-restapi.json +${cmd} ${CENTREON_PLUGINS} +... --plugin=apps::backup::rubrik::restapi::plugin +... --hostname=${HOSTNAME} +... --proto=http +... --mode=disks +... --port=${APIPORT} + + +*** Test Cases *** +jobs ${tc}/11 + [Tags] apps backup rubrik restapi jobs + + ${command} Catenate + ... ${cmd} + ... ${extraoptions} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} UNKNOWN: Need to specify either --service-account or --api-username/--api-password option. + ... 2 --service-account='client:12345' --secret='secret' OK + ... 3 --service-account='client:12345' --secret='secret' --use-cdm-authent OK