From 321806011fbf70c19fd14a0fbcc3ef24629244e5 Mon Sep 17 00:00:00 2001 From: Evan-Adam <152897682+Evan-Adam@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:25:35 +0100 Subject: [PATCH] [CTOR-284] new Datacore plugin with rest api (#4896) Co-authored-by: Sophie Depassio --- .github/scripts/stopwords.t | 5 +- .github/workflows/tests-functional.yml | 4 +- doc/en/developer/plugins_advanced.md | 4 +- .../deb.json | 4 + .../pkg.json | 9 + .../rpm.json | 4 + src/centreon/plugins/misc.pm | 8 + src/storage/datacore/restapi/custom/api.pm | 172 +++++++++++++++++ src/storage/datacore/restapi/mode/alerts.pm | 174 +++++++++++++++++ src/storage/datacore/restapi/mode/listpool.pm | 115 +++++++++++ .../datacore/restapi/mode/monitorstatus.pm | 148 +++++++++++++++ .../datacore/restapi/mode/poolspaceusage.pm | 121 ++++++++++++ src/storage/datacore/restapi/plugin.pm | 27 +++ ...l.robot => cloud-aws-cloudtrail.robot.old} | 12 +- ...ure-policyinsights-policystates.robot.old} | 21 +- .../api/storage-datacore-restapi.robot | 77 ++++++++ .../mockoon/storage-datacore-api.json | 174 +++++++++++++++++ .../mockoon/storage-datacore-restapi.json | 179 ++++++++++++++++++ 18 files changed, 1232 insertions(+), 26 deletions(-) create mode 100644 packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/deb.json create mode 100644 packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/pkg.json create mode 100644 packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/rpm.json create mode 100644 src/storage/datacore/restapi/custom/api.pm create mode 100644 src/storage/datacore/restapi/mode/alerts.pm create mode 100644 src/storage/datacore/restapi/mode/listpool.pm create mode 100644 src/storage/datacore/restapi/mode/monitorstatus.pm create mode 100644 src/storage/datacore/restapi/mode/poolspaceusage.pm create mode 100644 src/storage/datacore/restapi/plugin.pm rename tests/functional/api/{cloud-aws-cloudtrail.robot => cloud-aws-cloudtrail.robot.old} (97%) rename tests/functional/api/{cloud-azure-policyinsights-policystates.robot => cloud-azure-policyinsights-policystates.robot.old} (87%) create mode 100644 tests/functional/api/storage-datacore-restapi.robot create mode 100644 tests/resources/mockoon/storage-datacore-api.json create mode 100644 tests/resources/mockoon/storage-datacore-restapi.json diff --git a/.github/scripts/stopwords.t b/.github/scripts/stopwords.t index d363f846b..9c5c5a58b 100644 --- a/.github/scripts/stopwords.t +++ b/.github/scripts/stopwords.t @@ -1,14 +1,17 @@ --filter-vdom --force-counters32 Centreon -Fortinet +Datacore Fortigate +Fortinet license-instances-usage-prct MBean OID oneaccess-sys-mib perfdata powershell +proto +Sansymphony queue-messages-inflighted SNMP space-usage-prct diff --git a/.github/workflows/tests-functional.yml b/.github/workflows/tests-functional.yml index ab6de004e..c8f470c97 100644 --- a/.github/workflows/tests-functional.yml +++ b/.github/workflows/tests-functional.yml @@ -30,10 +30,10 @@ jobs: - name: Install Node.js uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2 with: - node-version: 16 + node-version: 20 - name: Install Mockoon CLI - run: npm install -g -D @mockoon/cli@3.1.0 + run: npm install -g -D @mockoon/cli@6.2.0 - name: Install perl dependencies uses: shogo82148/actions-setup-perl@28eae78d12c2bba1163aec45d123f6d9228bc307 # v1.29.0 diff --git a/doc/en/developer/plugins_advanced.md b/doc/en/developer/plugins_advanced.md index f4dda783e..b5f8a8a4b 100644 --- a/doc/en/developer/plugins_advanced.md +++ b/doc/en/developer/plugins_advanced.md @@ -943,9 +943,9 @@ List of methods: * **new**: class constructor. Overload if you need to add some specific options or to use a statefile. * **check_options**: overload if you need to check your specific options. -* **manage_selection**: overload if *mandatory*. Method to get informations for +* **manage_selection**: overload is *mandatory*. Method to get informations for the equipment. -* **set_counters**: overload if **mandatory**. Method to configure counters. +* **set_counters**: overload is **mandatory**. Method to configure counters. **Class hardware** diff --git a/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/deb.json b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/deb.json new file mode 100644 index 000000000..9757fe112 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/deb.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/pkg.json b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/pkg.json new file mode 100644 index 000000000..824189ab2 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/pkg.json @@ -0,0 +1,9 @@ +{ + "pkg_name": "centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi", + "pkg_summary": "Centreon Plugin", + "plugin_name": "centreon_datacore_api.pl", + "files": [ + "centreon/plugins/script_custom.pm", + "storage/datacore/api/" + ] +} diff --git a/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/rpm.json b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/rpm.json new file mode 100644 index 000000000..9757fe112 --- /dev/null +++ b/packaging/centreon-plugin-Hardware-Storage-DataCore-Sansymphony-Restapi/rpm.json @@ -0,0 +1,4 @@ +{ + "dependencies": [ + ] +} diff --git a/src/centreon/plugins/misc.pm b/src/centreon/plugins/misc.pm index 94789397c..29d4f7df4 100644 --- a/src/centreon/plugins/misc.pm +++ b/src/centreon/plugins/misc.pm @@ -311,6 +311,14 @@ sub backtick { return (0, join("\n", @output), $return_code); } +sub is_empty { + my $value = shift; + if (!defined($value) or $value eq '') { + return 1; + } + return 0; +} + sub trim { my ($value) = $_[0]; diff --git a/src/storage/datacore/restapi/custom/api.pm b/src/storage/datacore/restapi/custom/api.pm new file mode 100644 index 000000000..4f0de4d1a --- /dev/null +++ b/src/storage/datacore/restapi/custom/api.pm @@ -0,0 +1,172 @@ +# +# 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 storage::datacore::restapi::custom::api; +use strict; +use warnings; +use centreon::plugins::http; +use centreon::plugins::statefile; +use JSON::XS; +use centreon::plugins::misc qw(empty); + +sub new { + my ($class, %options) = @_; + if (!defined($options{output})) { + print "Class Custom: Need to specify 'output' argument.\n"; + exit 3; + } + if (!defined($options{options})) { + $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); + $options{output}->option_exit(); + } + my $self = {}; + bless $self, $class; + + + $options{options}->add_options(arguments => { + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port', default => 443 }, + 'proto:s' => { name => 'proto', default => 'https' }, + 'timeout:s' => { name => 'timeout' }, + 'username:s' => { name => 'username' }, + 'password:s' => { name => 'password' }, + 'unknown-http-status:s' => { name => 'unknown_http_status' }, + 'warning-http-status:s' => { name => 'warning_http_status' }, + 'critical-http-status:s' => { name => 'critical_http_status' } + }); + + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} +sub set_defaults {} + +# hostname,username and password are required options +sub check_options { + my ($self, %options) = @_; + $self->{http}->set_options(%{$self->{option_results}}); + + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 50; + + if (centreon::plugins::misc::is_empty($self->{option_results}->{hostname})) { + $self->{output}->add_option_msg(short_msg => 'Please set hostname option'); + $self->{output}->option_exit(); + } + if (centreon::plugins::misc::is_empty($self->{option_results}->{username})) { + $self->{output}->add_option_msg(short_msg => 'Please set username option to authenticate against datacore rest api'); + $self->{output}->option_exit(); + } + if (centreon::plugins::misc::is_empty($self->{option_results}->{password})) { + $self->{output}->add_option_msg(short_msg => 'Please set password option to authenticate against datacore rest api'); + $self->{output}->option_exit(); + } + +} + +sub settings { + my ($self, %options) = @_; + + return if (defined($self->{settings_done})); + $self->{http}->add_header(key => 'ServerHost', value => $self->{option_results}->{hostname}); + $self->{http}->set_options(basic => 1, credentials => 1); + $self->{settings_done} = 1; +} + +# wrapper around centreon::plugins::http::request to add authentication and decode json. +# output : deserialized json from the api if not error found in http call. +sub request_api { + my ($self, %options) = @_; + + # datacore api require a ServerHost header with the hostname used to query the api to respond. + # authentication is http standard basic auth. + my $result = $self->{http}->request( + username => $self->{option_results}->{username}, + password => $self->{option_results}->{password}, + unknown_status => $self->{option_results}->{unknown_http_status}, + warning_status => $self->{option_results}->{warning_http_status}, + critical_status => $self->{option_results}->{critical_http_status}, + %options, + ); + # Declare a scalar to deserialize the JSON content string into a perl data structure + my $decoded_content; + eval { + $decoded_content = JSON::XS->new->decode($result); + }; + # Catch the error that may arise in case the data received is not JSON + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode JSON result"); + $self->{output}->option_exit(); + } + return $decoded_content; + +} +1; + + +__END__ + +=head1 NAME + +Datacore Sansymphony Rest API + +=head1 REST API OPTIONS + +Datacore Sansymphony Rest API + +=over 8 + +=item B<--hostname> + +Address of the Datacore server that hosts the API endpoint. + +=item B<--port> + +Port of the resource to connect to (default: 443). + +=item B<--proto> + +HTTP protocol, either http or https (default: 'https') + +=item B<--username> + +Username to access the endpoint. + +=item B<--password> + +Password to access the endpoint. + +=item B<--timeout> + +Set timeout in seconds (default: 10). + +=back + +=head1 DESCRIPTION + +B. diff --git a/src/storage/datacore/restapi/mode/alerts.pm b/src/storage/datacore/restapi/mode/alerts.pm new file mode 100644 index 000000000..9307ba416 --- /dev/null +++ b/src/storage/datacore/restapi/mode/alerts.pm @@ -0,0 +1,174 @@ +# +# 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 storage::datacore::restapi::mode::alerts; +use strict; +use warnings; +use centreon::plugins::misc qw(empty); + +use base qw(centreon::plugins::templates::counter); + +my %alerts_level = ('trace' => 0, 'info' => 1, 'warning' => 2, 'error' => 3); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'alerts', type => 0}, + ]; + $self->{maps_counters}->{alerts} = [ + { + label => 'error', + nlabel => 'datacore.event.error.count', + set => { + key_values => [ { name => 'error' } ], + output_template => 'number of error alerts : %s', + perfdatas => [ { template => '%d', min => 0 } ] + } + }, { + label => 'warning', + nlabel => 'datacore.alerts.warning.count', + set => { + key_values => [ { name => 'warning' } ], + output_template => 'number of warning alerts : %s', + perfdatas => [ { template => '%d', min => 0 } ] + } + }, { + label => 'info', + nlabel => 'datacore.alerts.info.count', + set => { + key_values => [ { name => 'info' } ], + output_template => 'number of info alerts : %s', + perfdatas => [ { template => '%d', min => 0 } ] + } + }, { + label => 'trace', + nlabel => 'datacore.alerts.trace.count', + set => { + key_values => [ { name => 'trace' } ], + output_template => 'number of trace alerts : %s', + perfdatas => [ { template => '%d', min => 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 => { + 'filter-server:s' => { name => 'filter_server' }, + 'max-alert-age:s' => { name => 'max_alert_age' } }); + + $self->{output} = $options{output}; + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + + my $alerts = $options{custom}->request_api( + url_path => '/RestService/rest.svc/1.0/alerts' + ); + + my %alerts_count = ( + 0 => { count => 0, list => [] }, + 1 => { count => 0, list => [] }, + 2 => { count => 0, list => [] }, + 3 => { count => 0, list => [] }); + + for my $alert (@$alerts) { + + # spec require to filter on time of the log. + $alert->{TimeStamp} =~ /\/Date\((\d+)\)\/$/; + my $alert_date = $1; + if (centreon::plugins::misc::is_empty($alert_date) or $alert_date !~ /^[0-9]*[.,]?\d*$/){ + $self->{output}->output_add(long_msg => "alert on $alert->{MachineName} have an invalid date : $alert->{TimeStamp}\n", debug => 1); + next; + } + + # filter on age of the alert with a user defined max age + next if (defined($self->{option_results}->{max_alert_age}) + and $alert_date < (time - $self->{option_results}->{max_alert_age}) * 1000); + # filter on the machine issuing the alert with a user defined regex + + if (!centreon::plugins::misc::is_empty($self->{option_results}->{filter_server}) + and $alert->{MachineName} !~ $self->{option_results}->{filter_server}) { + $self->{output}->output_add(long_msg => "excluding alert from machine $alert->{MachineName}\n", debug => 1); + next; + } + + $alerts_count{$alert->{Level}}->{count}++; + # we don't want to clog the long output, so we keep only the few first logs. + # we use a array instead of directly adding to long_output because we need to sort errors + if (scalar(@{$alerts_count{$alert->{Level}}->{list}}) < 50) { + push(@{$alerts_count{$alert->{Level}}->{list}}, $alert->{MessageText}) + } + } + + $self->{output}->output_add(long_msg => "error : " . join("\n", @{$alerts_count{$alerts_level{error}}->{list}})); + $self->{output}->output_add(long_msg => "warning : " . join("\n", @{$alerts_count{$alerts_level{warning}}->{list}})); + + + + $self->{alerts} = { + trace => $alerts_count{$alerts_level{trace}}->{count}, + info => $alerts_count{$alerts_level{info}}->{count}, + warning => $alerts_count{$alerts_level{warning}}->{count}, + error => $alerts_count{$alerts_level{error}}->{count}, + }; + +} + +1; + +__END__ + +=head1 MODE + +Check Datacore alerts number exposed through the rest api + +=over 8 + +=item B<--max-alert-age> + +filter alerts to check those newer than this parameter (s) + +=item B<--filter-server> + +Define which devices should be monitored based on the MachineName. This option will be treated as a regular expression. +By default all machine will be checked. + +=item B<--warning/critical-*> + +Warning and critical threshold on the number of alerts of a type before changing state. +Replace * with trace, alert, warning, or error. + +=back + + diff --git a/src/storage/datacore/restapi/mode/listpool.pm b/src/storage/datacore/restapi/mode/listpool.pm new file mode 100644 index 000000000..104dfe6cc --- /dev/null +++ b/src/storage/datacore/restapi/mode/listpool.pm @@ -0,0 +1,115 @@ +# +# 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 storage::datacore::restapi::mode::listpool; +use strict; +use warnings; + +use base qw(centreon::plugins::mode); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + $options{options}->add_options(arguments => { + 'filter-name:s' => { name => 'filter_name' }, + }); + + + return $self; +} +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); +} + +sub manage_selection { + my ($self, %options) = @_; + $self->{pools} = (); + my $pool_list = $options{custom}->request_api( + url_path => '/RestService/rest.svc/1.0/pools'); + + if (scalar(@$pool_list) == 0) { + $self->{output}->add_option_msg(short_msg => "No pool found in api response."); + $self->{output}->option_exit(); + } + for my $pool (@$pool_list) { + next if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' + && $pool->{Alias} !~ /$self->{option_results}->{filter_name}/); + push(@{$self->{pools}}, { + ExtendedCaption => $pool->{ExtendedCaption}, + Caption => $pool->{Caption}, + Id => $pool->{Id}, + Alias => $pool->{Alias}} + ); + } + +} + +sub run { + my ($self, %options) = @_; + $self->manage_selection(%options); + if (scalar $self->{pools} == 0) { + $self->{output}->add_option_msg(short_msg => "No pool found."); + $self->{output}->option_exit(); + } + foreach (sort @{$self->{pools}}) { + $self->{output}->output_add(long_msg => sprintf("[ExtendedCaption = %s] [Caption = %s] [Id = %s] [Alias = %s]", + $_->{ExtendedCaption}, $_->{Caption}, $_->{Id}, $_->{Alias})); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'List pools : ' + ); + $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 => ['ExtendedCaption', 'Caption', 'Id', 'Alias']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + + foreach (sort @{$self->{pools}}) { + $self->{output}->add_disco_entry(ExtendedCaption => $_->{ExtendedCaption}, Caption => $_->{Caption}, + Id => $_->{Id}, Id => $_->{Id}, Alias => $_->{Alias}); + } +} +1; + +=head1 MODE + +List pools. + +=over 8 + +=item B<--filter-name> + +Filter pool name (can be a regexp). + +=back + +=cut \ No newline at end of file diff --git a/src/storage/datacore/restapi/mode/monitorstatus.pm b/src/storage/datacore/restapi/mode/monitorstatus.pm new file mode 100644 index 000000000..faf5f5515 --- /dev/null +++ b/src/storage/datacore/restapi/mode/monitorstatus.pm @@ -0,0 +1,148 @@ +# +# 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 storage::datacore::restapi::mode::monitorstatus; +use strict; +use warnings; + +use base qw(centreon::plugins::templates::counter); +use JSON::XS; +use Digest::MD5 qw(md5_hex); +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); +use centreon::plugins::misc qw(empty); + +my %monitor_state = (1 => "Undefined", 2 => "Healthy", 4 => "Attention", 8 => "Warning", 16 => "Critical"); + +sub custom_status_output { + my ($self, %options) = @_; + my $res = sprintf( + "'%s' status : '%s', message is '%s'", + $self->{result_values}->{extendedcaption}, + $self->{result_values}->{state}, + $self->{result_values}->{messagetext} + ); + return $res; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 1, message_multiple => 'All memory usages are ok', skipped_code => { -10 => 1 } } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'state', + type => 2, + warning_default => '%{state} =~ /Warning/i', + critical_default => '%{state} =~ /Critical/i', + set => { + key_values => [ { name => 'state' }, { name => 'extendedcaption' }, { name => 'messagetext' } ], + closure_custom_output => $self->can('custom_status_output'), + closure_custom_perfdata => sub {return 0;}, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + $options{options}->add_options(arguments => { + 'filter-caption:s' => { name => 'filter_caption' } }); + $self->{cache} = centreon::plugins::statefile->new(%options); + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + $self->{cache}->check_options(option_results => $self->{option_results}); +} + +sub manage_selection { + my ($self, %options) = @_; + + if ($self->{cache}->read('statefile' => 'datacore_api_monitors' . md5_hex($self->{option_results}->{hostname} . $self->{option_results}->{username})) + and $self->{cache}->get(name => 'expires_on') > time() + 1) { + + return $self->{cache}->get(name => 'monitor_data'); + } + + my $monitor_data = $options{custom}->request_api( + url_path => '/RestService/rest.svc/1.0/monitors'); + + my $datas = { last_timestamp => time(), monitor_data => $monitor_data, expires_on => time() + 60 }; + $self->{cache}->write(data => $datas); + + my $monitored_count = 0; + for my $object (@$monitor_data) { + + if (!centreon::plugins::misc::is_empty($self->{option_results}->{filter_caption}) + and $object->{ExtendedCaption} !~ $self->{option_results}->{filter_caption}) { + next; + } + $self->{global}->{$monitored_count} = { + state => $monitor_state{$object->{State}}, + messagetext => $object->{MessageText}, + extendedcaption => $object->{ExtendedCaption} + }; + $monitored_count++; + } + # for now if no data is given to the counter, output is OK: with status ok, instead of unknown. + # We manage this case in each plugin for now. + if ($monitored_count == 0) { + $self->{output}->add_option_msg(short_msg => 'No monitors where checked, please check filter_caption parameter and api response.'); + $self->{output}->option_exit(); + } +} + +# as --filter-caption allow to filter element to check and this api don't allow parameter filtering, we should cache +# the output in case a client make multiples check a minute. + +1; + +__END__ + +=head1 MODE + +Check Datacore monitor status exposed through the rest api. + +=over 8 + +=item B<--filter-caption> + +Define which element should be monitored based on the extended caption. This option will be treated as a regular expression. +By default all elements will be checked. + +=item B<--warning-state> B<--critical-state> + +define which output from the api should be considered warning or critical. + +warning_default = '%{state} =~ /Warning/i', + +critical_default = '%{state} =~ /Critical/i', + +possible value for state : Undefined, Healthy, Attention, Warning, Critical. + +=back + diff --git a/src/storage/datacore/restapi/mode/poolspaceusage.pm b/src/storage/datacore/restapi/mode/poolspaceusage.pm new file mode 100644 index 000000000..3fe233bc7 --- /dev/null +++ b/src/storage/datacore/restapi/mode/poolspaceusage.pm @@ -0,0 +1,121 @@ +# +# 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 storage::datacore::restapi::mode::poolspaceusage; +use strict; +use warnings; + +use base qw(centreon::plugins::templates::counter); +use JSON::XS; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + # BytesAllocatedPercentage is the disk usage of the pool + # in datacore you can make thin provisionning, where you give to each partition more than what you really have. + # oversubscribed is the number of Bytes allocated minus the number of bytes present in the system. + { name => 'BytesAllocatedPercentage', type => 0 }, + { name => 'oversubscribed', type => 0 }, + ]; + $self->{maps_counters}->{BytesAllocatedPercentage} = [ + + { + label => 'bytesallocatedpercentage', + nlabel => 'datacore.pool.bytesallocated.percentage', + set => { + key_values => [ { name => 'bytesallocatedpercentage' } ], + output_template => 'Bytes Allocated : %s %%', + perfdatas => [ + { template => '%d', unit => '%', min => 0, max => 100 } + ] + } + } ]; + $self->{maps_counters}->{oversubscribed} = [ { + label => 'oversubscribed', + nlabel => 'datacore.pool.oversubscribed.bytes', + set => { + key_values => [ { name => 'oversubscribed' } ], + output_template => 'Over subscribed bytes : %s', + perfdatas => [ + { template => '%d', unit => 'bytes', min => 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 => { + 'pool-id:s' => { name => 'pool_id' } }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + if (centreon::plugins::misc::is_empty($self->{option_results}->{pool_id})) { + $self->{output}->add_option_msg(short_msg => 'Please set pool-id option'); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $data = $options{custom}->request_api( + url_path => '/RestService/rest.svc/1.0/performances/' . $self->{option_results}->{pool_id}, + ); + if (defined($data->[1])) { + $self->{output}->add_option_msg(short_msg => 'multiples pools found in api response, only one is expected. Please check pool_id and datacore versions.'); + $self->{output}->option_exit(); + } + $self->{BytesAllocatedPercentage}->{bytesallocatedpercentage} = $data->[0]->{"BytesAllocatedPercentage"}; + $self->{oversubscribed}->{oversubscribed} = $data->[0]->{"BytesOverSubscribed"}; + +} +1; + +__END__ + +=head1 MODE + +Check Datacore pool space and over subscribed usage exposed through the rest api. + +=over 8 + +=item B<--pool-id> + +Id of the pool to check. See list-pool autodiscovery mode to list pools id (required). + +=item B<--warning-oversubscribed> B<--critical-oversubscribed> + +Warning and critical threshold on the number of Bytes suscribed over the real space of the pool. + +=item B<--warning-bytesallocatedpercentage> B<--critical-bytesallocatedpercentage> + +Warning and critical threshold on the percentage of bytes allocated in the pool. + +=back + + diff --git a/src/storage/datacore/restapi/plugin.pm b/src/storage/datacore/restapi/plugin.pm new file mode 100644 index 000000000..98aed2f64 --- /dev/null +++ b/src/storage/datacore/restapi/plugin.pm @@ -0,0 +1,27 @@ +package storage::datacore::restapi::plugin; +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + # A version, we don't really use it but could help if your want to version your code + $self->{version} = '0.1'; + + $self->{modes} = { + 'pool-usage' => 'storage::datacore::restapi::mode::poolspaceusage', + 'alerts' => 'storage::datacore::restapi::mode::alerts', + 'list-pool' => 'storage::datacore::restapi::mode::listpool', + 'status-monitor' => 'storage::datacore::restapi::mode::monitorstatus', + }; + $self->{custom_modes}->{api} = 'storage::datacore::restapi::custom::api'; + return $self; +} +1; +=head1 PLUGIN DESCRIPTION + +Check Datacore using rest API. + diff --git a/tests/functional/api/cloud-aws-cloudtrail.robot b/tests/functional/api/cloud-aws-cloudtrail.robot.old similarity index 97% rename from tests/functional/api/cloud-aws-cloudtrail.robot rename to tests/functional/api/cloud-aws-cloudtrail.robot.old index 2252800df..445a0de15 100644 --- a/tests/functional/api/cloud-aws-cloudtrail.robot +++ b/tests/functional/api/cloud-aws-cloudtrail.robot.old @@ -153,6 +153,7 @@ AWS CloudTrail check trail status FOR ${checktrailstatus_value} IN @{checktrailstatus_values} ${output} Run ... ${CMD} --mode=checktrailstatus --endpoint=http://localhost:3000/cloudtrail/gettrailstatus/${checktrailstatus_value.trailstatus} --trail-name=${checktrailstatus_value.trailname} + ${output} Strip String ${output} Should Be Equal As Strings ... ${output} ... ${checktrailstatus_value.result} @@ -188,6 +189,7 @@ AWS CloudTrail count events ${command} Catenate ${command} --critical-count=${countevents_value.criticalcount} END ${output} Run ${command} + ${output} Strip String ${output} Should Be Equal As Strings ... ${output} ... ${countevents_value.result} @@ -204,13 +206,7 @@ Start Mockoon ... ${MOCKOON_JSON} ... --port ... 3000 - ... --pname - ... aws-cloudtrail - Wait For Process ${process} + Sleep 5s Stop Mockoon - ${process} Start Process - ... mockoon-cli - ... stop - ... mockoon-aws-cloudtrail - Wait For Process ${process} + Terminate All Processes diff --git a/tests/functional/api/cloud-azure-policyinsights-policystates.robot b/tests/functional/api/cloud-azure-policyinsights-policystates.robot.old similarity index 87% rename from tests/functional/api/cloud-azure-policyinsights-policystates.robot rename to tests/functional/api/cloud-azure-policyinsights-policystates.robot.old index 51b5851f7..5b9bd61a6 100644 --- a/tests/functional/api/cloud-azure-policyinsights-policystates.robot +++ b/tests/functional/api/cloud-azure-policyinsights-policystates.robot.old @@ -14,29 +14,29 @@ Test Timeout 120s ${CENTREON_PLUGINS} ${CURDIR}${/}..${/}..${/}..${/}src${/}centreon_plugins.pl ${MOCKOON_JSON} ${CURDIR}${/}..${/}..${/}resources${/}mockoon${/}cloud-azure-policyinsights-policystates.json -${LOGIN_ENDPOINT} http://localhost:3000/login +${LOGIN_ENDPOINT} http://localhost:3001/login ${CMD} perl ${CENTREON_PLUGINS} --plugin=cloud::azure::policyinsights::policystates::plugin --subscription=subscription --tenant=tenant --client-id=client_id --client-secret=secret --login-endpoint=${LOGIN_ENDPOINT} &{compliance_value1} -... endpoint=http://localhost:3000/ok +... endpoint=http://localhost:3001/ok ... policyname= ... resourcelocation= ... resourcetype= ... result=OK: Number of non compliant policies: 0 - All compliances states are ok | 'policies.non_compliant.count'=0;;;0; &{compliance_value2} -... endpoint=http://localhost:3000/oknextlink +... endpoint=http://localhost:3001/oknextlink ... policyname=9daedab3-fb2d-461e-b861-71790eead4f6 ... resourcelocation= ... resourcetype= ... result=OK: Number of non compliant policies: 0 - All compliances states are ok | 'policies.non_compliant.count'=0;;;0; &{compliance_value3} -... endpoint=http://localhost:3000/nok1 +... endpoint=http://localhost:3001/nok1 ... policyname=9daedab3-fb2d-461e-b861-71790eead4f6 ... resourcelocation=fr ... resourcetype= ... result=CRITICAL: Compliance state for policy '9daedab3-fb2d-461e-b861-71790eead4f6' on resource 'mypubip1' is 'NonCompliant' | 'policies.non_compliant.count'=1;;;0; &{compliance_value4} -... endpoint=http://localhost:3000/nok2 +... endpoint=http://localhost:3001/nok2 ... policyname=9daedab3-fb2d-461e-b861-71790eead4f6 ... resourcelocation=fr ... resourcetype=ip @@ -66,6 +66,7 @@ Azure PolicyInsights PolicyStates compliance ${command} Catenate ${command} --resource-type=${compliance_value.resourcetype} END ${output} Run ${command} + ${output} Strip String ${output} Should Be Equal As Strings ... ${output} ... ${compliance_value.result} @@ -82,13 +83,7 @@ Start Mockoon ... ${MOCKOON_JSON} ... --port ... 3000 - ... --pname - ... azure-policyinsights - Wait For Process ${process} + Sleep 5s Stop Mockoon - ${process} Start Process - ... mockoon-cli - ... stop - ... mockoon-azure-policyinsights - Wait For Process ${process} + Terminate All Processes diff --git a/tests/functional/api/storage-datacore-restapi.robot b/tests/functional/api/storage-datacore-restapi.robot new file mode 100644 index 000000000..c1b95d661 --- /dev/null +++ b/tests/functional/api/storage-datacore-restapi.robot @@ -0,0 +1,77 @@ +*** Settings *** +Documentation datacore rest api plugin + +Library Examples +Library OperatingSystem +Library Process +Library String + +Suite Setup Start Mockoon +Suite Teardown Stop Mockoon +Test Timeout 120s + +*** Variables *** +${CENTREON_PLUGINS} ${CURDIR}${/}..${/}..${/}..${/}src${/}centreon_plugins.pl +${MOCKOON_JSON} ${CURDIR}${/}..${/}..${/}resources${/}mockoon${/}storage-datacore-restapi.json + +${CMD} perl ${CENTREON_PLUGINS} --plugin=storage::datacore::restapi::plugin --password=pass --username=user --port=3000 --hostname=127.0.0.1 --proto=http + + +*** Test Cases *** +Datacore check pool usage + [Documentation] Check Datacore pool usage + [Tags] storage api + ${output} Run + ... ${CMD} --mode=pool-usage --critical-oversubscribed=${critical-oversubscribed} --warning-oversubscribed=${warning-oversubscribed} --warning-bytesallocatedpercentage=${warning-bytesallocatedpercentage} --critical-bytesallocatedpercentage=${critical-bytesallocatedpercentage} --pool-id=B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D:{4dec1b5a-2577-11e5-80c3-00155d651622} + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${result} + ... Wrong output result for pool usage :\n\n ${output} \n\n ${result}\n\n + + Examples: warning-bytesallocatedpercentage critical-bytesallocatedpercentage warning-oversubscribed critical-oversubscribed result -- + ... 2 5 -1 3 CRITICAL: Bytes Allocated : 12 % WARNING: Over subscribed bytes : 0 | 'datacore.pool.bytesallocated.percentage'=12%;0:2;0:5;0;100 'datacore.pool.oversubscribed.bytes'=0bytes;0:-1;0:3;0; + ... 70 80 10 20 OK: Bytes Allocated : 12 % - Over subscribed bytes : 0 | 'datacore.pool.bytesallocated.percentage'=12%;0:70;0:80;0;100 'datacore.pool.oversubscribed.bytes'=0bytes;0:10;0:20;0; + +Datacore check alert count + [Documentation] Check Datacore pool usage + [Tags] storage api + ${output} Run + ... ${CMD} --mode=alerts --warning-error=${warning-error} --critical-error=${critical-error} --warning-warning=${warning-warning} --critical-warning=${critical-warning} + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${result} + ... Wrong output result for alert count :\n\n ${output} \n\n ${result}\n\n + + Examples: warning-error critical-error warning-warning critical-warning result -- + ... 0 1 5 5 WARNING: number of error alerts : 1 | 'datacore.event.error.count'=1;0:0;0:1;0; 'datacore.alerts.warning.count'=1;0:5;0:5;0; 'datacore.alerts.info.count'=0;;;0; 'datacore.alerts.trace.count'=0;;;0; + ... 5 5 5 5 OK: number of error alerts : 1, number of warning alerts : 1, number of info alerts : 0, number of trace alerts : 0 | 'datacore.event.error.count'=1;0:5;0:5;0; 'datacore.alerts.warning.count'=1;0:5;0:5;0; 'datacore.alerts.info.count'=0;;;0; 'datacore.alerts.trace.count'=0;;;0; + +Datacore check status monitor + [Documentation] Check Datacore pool usage + [Tags] storage api + ${output} Run + ... ${CMD} --mode=status-monitor + ${output} Strip String ${output} + Should Be Equal As Strings + ... ${output} + ... ${result} + ... Wrong output result for status monitor :\n${output} \nresult:\n${result}\n\n + + Examples: result -- + ... CRITICAL: 'State of HostVM2' status : 'Critical', message is 'Connected' + +*** Keywords *** +Start Mockoon + ${process} Start Process + ... mockoon-cli + ... start + ... --data + ... ${MOCKOON_JSON} + ... --port + ... 3000 + Sleep 5s + +Stop Mockoon + Terminate All Processes \ No newline at end of file diff --git a/tests/resources/mockoon/storage-datacore-api.json b/tests/resources/mockoon/storage-datacore-api.json new file mode 100644 index 000000000..15938230b --- /dev/null +++ b/tests/resources/mockoon/storage-datacore-api.json @@ -0,0 +1,174 @@ +{ + "uuid": "317849df-2152-4b89-88f7-f22ea37aab32", + "lastMigration": 27, + "name": "Storage datacore api", + "endpointPrefix": "", + "latency": 0, + "port": 3000, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "9d203639-fb39-402b-9759-91c156e247c6", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/pools", + "responses": [ + { + "uuid": "f8d1590b-878f-4884-9214-dea80580eb75", + "body": "[{\r\n \"Caption\" : \"Disk pool 1\",\r\n \"ExtendedCaption\" : \"Disk pool 1 on StorageSvr001\",\r\n \"Id\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551:{d68b8a36-0ec4-11e5-80ba-00155d651622}\",\r\n \"Internal\" : false,\r\n \"SequenceNumber\" : 11963,\r\n \"Alias\" : \"Disk pool 1\",\r\n \"AutoTieringEnabled\" : true,\r\n \"ChunkSize\" : {\r\n \"Value\" : 134217728\r\n },\r\n \"Description\" : null,\r\n \"InSharedMode\" : false,\r\n \"IsAuthorized\" : true,\r\n \"MaxTierNumber\" : 3,\r\n \"PoolMode\" : 1,\r\n \"PoolStatus\" : 0,\r\n \"PresenceStatus\" : 1,\r\n \"SMPAApproved\" : false,\r\n \"ServerId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\r\n \"SharedPoolId\" : null,\r\n \"TierReservedPct\" : 0,\r\n \"Type\" : 0,\r\n \"IsBulk\" : false\r\n},\r\n{\r\n \"Caption\" : \"Shared pool 1\",\r\n \"ExtendedCaption\" : \"Shared pool 1 on StorageSvr002\",\r\n \"Id\" : \"B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D:{4dec1b5a-2577-11e5-80c3-00155d651622}\",\r\n \"Internal\" : false,\r\n \"SequenceNumber\" : 11967,\r\n \"Alias\" : \"Shared pool 1\",\r\n \"AutoTieringEnabled\" : true,\r\n \"ChunkSize\" : {\r\n \"Value\" : 134217728\r\n },\r\n \"Description\" : null,\r\n \"InSharedMode\" : true,\r\n \"IsAuthorized\" : true,\r\n \"MaxTierNumber\" : 3,\r\n \"PoolMode\" : 1,\r\n \"PoolStatus\" : 0,\r\n \"PresenceStatus\" : 1,\r\n \"SMPAApproved\" : true,\r\n \"ServerId\" : \"B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D\",\r\n \"SharedPoolId\" : \"{4dec1b5a-2577-11e5-80c3-00155d651622}\",\r\n \"TierReservedPct\" : 0,\r\n \"Type\" : 0,\r\n \"IsBulk\" : false\r\n} ]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true + } + ], + "enabled": true, + "responseMode": null + }, + { + "uuid": "8837f5cd-5ca3-4779-ba8e-6b46692c487b", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/performances/B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D:%7B4dec1b5a-2577-11e5-80c3-00155d651622%7D", + "responses": [ + { + "uuid": "a5bf53e2-5edf-437e-a684-68315cfa7824", + "body": "[\n {\n \"CollectionTime\": \"/Date(1486402608775)/\",\n \"NullCounterMap\": 0,\n \"BytesAllocated\": 6174015488,\n \"BytesAllocatedPercentage\": 12,\n \"BytesAvailable\": 47110422528,\n \"BytesAvailablePercentage\": 88,\n \"BytesInReclamation\": 0,\n \"BytesInReclamationPercentage\": 0,\n \"BytesOverSubscribed\": 0,\n \"BytesReserved\": 0,\n \"BytesReservedPercentage\": 0,\n \"BytesTotal\": 53284438016,\n \"EstimatedDepletionTime\": 2592000001,\n \"MaxPoolBytes\": 1108127332171776,\n \"MaxReadTime\" : 0,\n \"MaxReadWriteTime\" : 0,\n \"MaxWriteTime\" : 0,\n \"PercentAllocated\" : 12,\n \"PercentAvailable\" : 88,\n \"TotalBytesMigrated\" : 0,\n \"TotalBytesRead\" : 307200,\n \"TotalBytesTransferred\" : 2036756992,\n \"TotalBytesWritten\" : 2036470272,\n \"TotalOperations\" : 53002,\n \"TotalReadTime\" : 0,\n \"TotalReads\" : 18,\n \"TotalWriteTime\" : 74994,\n \"TotalWrites\" : 52998\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true + } + ], + "enabled": true, + "responseMode": null + }, + { + "uuid": "ce9e85c8-c72e-47d4-b70a-0fd3c3a6d703", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/alerts", + "responses": [ + { + "uuid": "b6425c83-2f13-4ffe-a9f6-c73feb54c2ec", + "body": "[ {\n \"Caller\" : \"ServerMachineGroup.UpdateServers\",\n \"HighPriority\" : true,\n \"Id\" : {\n \"MachineId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SequenceNumber\" : 34937\n },\n \"Level\" : 2,\n \"MachineName\" : \"STORAGESVR001\",\n \"MessageData\" : [ \"StorageSvr002\" ],\n \"MessageText\" : \"Connection to server {0} has been lost.\",\n \"NeedsAcknowledge\" : false,\n \"Sources\" : [ {\n \"SourceCaption\" : \"Group1\",\n \"SourceId\" : \"4b428832-fd51-45df-9c21-ce267e1ceb7a\",\n \"SourceType\" : 12\n } ],\n \"TimeStamp\" : \"/Date(1437398005546)/\",\n \"UserId\" : null,\n \"UserName\" : null,\n \"Visibility\" : 64\n}, {\n \"Caller\" : \"BasePerfMonitor`1.SetState\",\n \"HighPriority\" : true,\n \"Id\" : {\n \"MachineId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SequenceNumber\" : 33985\n },\n \"Level\" : 3,\n \"MachineName\" : \"STORAGESVR001\",\n \"MessageData\" : null,\n \"MessageText\" : \"The Replication buffer free space of StorageSvr001 in Group1 is <= 10%\",\n \"NeedsAcknowledge\" : false,\n \"Sources\" : [ {\n \"SourceCaption\" : \"Replication buffer free space of StorageSvr001 in Group1\",\n \"SourceId\" : \"FallingThresholdPerfMonitor`1ReplicationBufferPercentFreeSpace\",\n \"SourceType\" : 3\n }, {\n \"SourceCaption\" : \"StorageSvr001 in Group1\",\n \"SourceId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SourceType\" : 12\n }, {\n \"SourceCaption\" : \"Group1\",\n \"SourceId\" : \"4b428832-fd51-45df-9c21-ce267e1ceb7a\",\n \"SourceType\" : 12\n } ],\n \"TimeStamp\" : \"/Date(1437397587894)/\",\n \"UserId\" : null,\n \"UserName\" : null,\n \"Visibility\" : 64\n}]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true + } + ], + "enabled": true, + "responseMode": null + }, + { + "uuid": "a916f10f-b2f2-498a-9b87-3aeffd80c8dc", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/monitors", + "responses": [ + { + "uuid": "d2713bdd-33e9-415a-bffe-16e279b1b753", + "body": "[\n {\n \"Caption\": \"State\",\n \"ExtendedCaption\": \"State of HostVM2\",\n \"Id\": \"ClientMachineStateMonitor\",\n \"Internal\": false,\n \"SequenceNumber\": 9417,\n \"Description\": \"Monitors the state of hosts.\",\n \"MessageText\": \"Connected\",\n \"MonitoredObjectId\": \"ed89ef91d72142dc958e02c5b7014fac\",\n \"State\": 16,\n \"TemplateId\": \"T(DataCore.Executive.Controller.ClientMachineStateMonitor)\",\n \"TimeStamp\": \"/Date(1486136115475-0500)/\"\n },\n {\n \"Caption\": \"I/O latency\",\n \"ExtendedCaption\": \"I/O latency of Mirror of Virtual disk 1 on StorageSvr005\",\n \"Id\": \"RisingThresholdPerfMonitor`1TotalOperationsTime\",\n \"Internal\": false,\n \"SequenceNumber\": 10036,\n \"Description\": \"Monitors the physical disks latency.\",\n \"MessageText\": \"Disabled\",\n \"MonitoredObjectId\": \"V.{2f652562-e7e3-11e6-80d7-00155d651611}-00000001_N.22690030D9218568\",\n \"State\": 1,\n \"TemplateId\": \"T(DataCore.Executive.Controller.RisingThresholdPerfMonitor`1[T]TotalOperationsTime)\",\n \"TimeStamp\": \"/Date(1486148705910-0500)/\"\n }\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true + } + ], + "enabled": true, + "responseMode": null + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "9d203639-fb39-402b-9759-91c156e247c6" + }, + { + "type": "route", + "uuid": "8837f5cd-5ca3-4779-ba8e-6b46692c487b" + }, + { + "type": "route", + "uuid": "ce9e85c8-c72e-47d4-b70a-0fd3c3a6d703" + }, + { + "type": "route", + "uuid": "a916f10f-b2f2-498a-9b87-3aeffd80c8dc" + } + ], + "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": [] +} \ No newline at end of file diff --git a/tests/resources/mockoon/storage-datacore-restapi.json b/tests/resources/mockoon/storage-datacore-restapi.json new file mode 100644 index 000000000..f7aae507a --- /dev/null +++ b/tests/resources/mockoon/storage-datacore-restapi.json @@ -0,0 +1,179 @@ +{ + "uuid": "8cba072c-7794-4ff7-ab36-158ef1abed39", + "lastMigration": 32, + "name": "Storage datacore api", + "endpointPrefix": "", + "latency": 0, + "port": 3001, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "148371c5-aaa7-4bf0-a818-e81a21aa7f9a", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/pools", + "responses": [ + { + "uuid": "003eae6e-f58f-44db-bd03-3b44ca578046", + "body": "[ {\n \"Caption\" : \"Disk pool 1\",\n \"ExtendedCaption\" : \"Disk pool 1 on StorageSvr001\",\n \"Id\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551:{d68b8a36-0ec4-11e5-80ba-00155d651622}\",\n \"Internal\" : false,\n \"SequenceNumber\" : 11963,\n \"Alias\" : \"Disk pool 1\",\n \"AutoTieringEnabled\" : true,\n \"ChunkSize\" : {\n \"Value\" : 134217728\n },\n \"Description\" : null,\n \"InSharedMode\" : false,\n \"IsAuthorized\" : true,\n \"MaxTierNumber\" : 3,\n \"PoolMode\" : 1,\n \"PoolStatus\" : 0,\n \"PresenceStatus\" : 1,\n \"SMPAApproved\" : false,\n \"ServerId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SharedPoolId\" : null,\n \"TierReservedPct\" : 0,\n \"Type\" : 0,\n \"IsBulk\" : false\n},\n{\n \"Caption\" : \"Shared pool 1\",\n \"ExtendedCaption\" : \"Shared pool 1 on StorageSvr002\",\n \"Id\" : \"B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D:{4dec1b5a-2577-11e5-80c3-00155d651622}\",\n \"Internal\" : false,\n \"SequenceNumber\" : 11967,\n \"Alias\" : \"Shared pool 1\",\n \"AutoTieringEnabled\" : true,\n \"ChunkSize\" : {\n \"Value\" : 134217728\n },\n \"Description\" : null,\n \"InSharedMode\" : true,\n \"IsAuthorized\" : true,\n \"MaxTierNumber\" : 3,\n \"PoolMode\" : 1,\n \"PoolStatus\" : 0,\n \"PresenceStatus\" : 1,\n \"SMPAApproved\" : true,\n \"ServerId\" : \"B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D\",\n \"SharedPoolId\" : \"{4dec1b5a-2577-11e5-80c3-00155d651622}\",\n \"TierReservedPct\" : 0,\n \"Type\" : 0,\n \"IsBulk\" : false\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": "0c038179-3723-4d67-9bed-fc226629c847", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/performances/B5C140F5-6B13-4CAD-AF9D-F7C4172B3A1D:%7B4dec1b5a-2577-11e5-80c3-00155d651622%7D", + "responses": [ + { + "uuid": "0eb0a2ca-2f12-40af-ba94-0feef989b6a1", + "body": "[\n {\n \"CollectionTime\": \"/Date(1486402608775)/\",\n \"NullCounterMap\": 0,\n \"BytesAllocated\": 6174015488,\n \"BytesAllocatedPercentage\": 12,\n \"BytesAvailable\": 47110422528,\n \"BytesAvailablePercentage\": 88,\n \"BytesInReclamation\": 0,\n \"BytesInReclamationPercentage\": 0,\n \"BytesOverSubscribed\": 0,\n \"BytesReserved\": 0,\n \"BytesReservedPercentage\": 0,\n \"BytesTotal\": 53284438016,\n \"EstimatedDepletionTime\": 2592000001,\n \"MaxPoolBytes\": 1108127332171776,\n \"MaxReadTime\" : 0,\n \"MaxReadWriteTime\" : 0,\n \"MaxWriteTime\" : 0,\n \"PercentAllocated\" : 12,\n \"PercentAvailable\" : 88,\n \"TotalBytesMigrated\" : 0,\n \"TotalBytesRead\" : 307200,\n \"TotalBytesTransferred\" : 2036756992,\n \"TotalBytesWritten\" : 2036470272,\n \"TotalOperations\" : 53002,\n \"TotalReadTime\" : 0,\n \"TotalReads\" : 18,\n \"TotalWriteTime\" : 74994,\n \"TotalWrites\" : 52998\n }\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": "b9f7dd2b-e1e1-4902-95ba-c6b76a1de390", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/alerts", + "responses": [ + { + "uuid": "a1dece29-074b-4100-a764-f7ddccdec8da", + "body": "[ {\n \"Caller\" : \"ServerMachineGroup.UpdateServers\",\n \"HighPriority\" : true,\n \"Id\" : {\n \"MachineId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SequenceNumber\" : 34937\n },\n \"Level\" : 2,\n \"MachineName\" : \"STORAGESVR001\",\n \"MessageData\" : [ \"StorageSvr002\" ],\n \"MessageText\" : \"Connection to server {0} has been lost.\",\n \"NeedsAcknowledge\" : false,\n \"Sources\" : [ {\n \"SourceCaption\" : \"Group1\",\n \"SourceId\" : \"4b428832-fd51-45df-9c21-ce267e1ceb7a\",\n \"SourceType\" : 12\n } ],\n \"TimeStamp\" : \"/Date(1437398005546)/\",\n \"UserId\" : null,\n \"UserName\" : null,\n \"Visibility\" : 64\n}, {\n \"Caller\" : \"BasePerfMonitor`1.SetState\",\n \"HighPriority\" : true,\n \"Id\" : {\n \"MachineId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SequenceNumber\" : 33985\n },\n \"Level\" : 3,\n \"MachineName\" : \"STORAGESVR001\",\n \"MessageData\" : null,\n \"MessageText\" : \"The Replication buffer free space of StorageSvr001 in Group1 is <= 10%\",\n \"NeedsAcknowledge\" : false,\n \"Sources\" : [ {\n \"SourceCaption\" : \"Replication buffer free space of StorageSvr001 in Group1\",\n \"SourceId\" : \"FallingThresholdPerfMonitor`1ReplicationBufferPercentFreeSpace\",\n \"SourceType\" : 3\n }, {\n \"SourceCaption\" : \"StorageSvr001 in Group1\",\n \"SourceId\" : \"A3E78CFA-DBEC-44E1-A23C-E9E262EC4551\",\n \"SourceType\" : 12\n }, {\n \"SourceCaption\" : \"Group1\",\n \"SourceId\" : \"4b428832-fd51-45df-9c21-ce267e1ceb7a\",\n \"SourceType\" : 12\n } ],\n \"TimeStamp\" : \"/Date(1437397587894)/\",\n \"UserId\" : null,\n \"UserName\" : null,\n \"Visibility\" : 64\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": "be0b350e-f8da-4998-a045-a3cf840745c0", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "RestService/rest.svc/1.0/monitors", + "responses": [ + { + "uuid": "6f53cd5e-7417-4fdf-a310-e2a7af4b7c3a", + "body": "[\r\n {\r\n \"Caption\": \"State\",\r\n \"ExtendedCaption\": \"State of HostVM2\",\r\n \"Id\": \"ClientMachineStateMonitor\",\r\n \"Internal\": false,\r\n \"SequenceNumber\": 9417,\r\n \"Description\": \"Monitors the state of hosts.\",\r\n \"MessageText\": \"Connected\",\r\n \"MonitoredObjectId\": \"ed89ef91d72142dc958e02c5b7014fac\",\r\n \"State\": 16,\r\n \"TemplateId\": \"T(DataCore.Executive.Controller.ClientMachineStateMonitor)\",\r\n \"TimeStamp\": \"/Date(1486136115475-0500)/\"\r\n },\r\n {\r\n \"Caption\": \"I/O latency\",\r\n \"ExtendedCaption\": \"I/O latency of Mirror of Virtual disk 1 on StorageSvr005\",\r\n \"Id\": \"RisingThresholdPerfMonitor`1TotalOperationsTime\",\r\n \"Internal\": false,\r\n \"SequenceNumber\": 10036,\r\n \"Description\": \"Monitors the physical disks latency.\",\r\n \"MessageText\": \"Disabled\",\r\n \"MonitoredObjectId\": \"V.{2f652562-e7e3-11e6-80d7-00155d651611}-00000001_N.22690030D9218568\",\r\n \"State\": 1,\r\n \"TemplateId\": \"T(DataCore.Executive.Controller.RisingThresholdPerfMonitor`1[T]TotalOperationsTime)\",\r\n \"TimeStamp\": \"/Date(1486148705910-0500)/\"\r\n }\r\n]", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "148371c5-aaa7-4bf0-a818-e81a21aa7f9a" + }, + { + "type": "route", + "uuid": "0c038179-3723-4d67-9bed-fc226629c847" + }, + { + "type": "route", + "uuid": "b9f7dd2b-e1e1-4902-95ba-c6b76a1de390" + }, + { + "type": "route", + "uuid": "be0b350e-f8da-4998-a045-a3cf840745c0" + } + ], + "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": [], + "callbacks": [] +} \ No newline at end of file