From 2f5a8d757bcefff7c52d87e9695374117af5b10e Mon Sep 17 00:00:00 2001 From: Thibault S <48209914+thibaults-centreon@users.noreply.github.com> Date: Tue, 12 Jan 2021 13:07:20 +0100 Subject: [PATCH] add(plugin): Azure Elastic Pools (#2482) --- .../azure/database/elasticpool/mode/cpu.pm | 293 +++++++++++++++++ .../database/elasticpool/mode/discovery.pm | 60 ++++ .../azure/database/elasticpool/mode/dtu.pm | 293 +++++++++++++++++ .../azure/database/elasticpool/mode/log.pm | 296 +++++++++++++++++ .../database/elasticpool/mode/sessions.pm | 286 +++++++++++++++++ .../database/elasticpool/mode/storage.pm | 302 ++++++++++++++++++ .../azure/database/elasticpool/plugin.pm | 65 ++++ 7 files changed, 1595 insertions(+) create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/cpu.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/discovery.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/dtu.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/log.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/sessions.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/mode/storage.pm create mode 100644 centreon-plugins/cloud/azure/database/elasticpool/plugin.pm diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/cpu.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/cpu.pm new file mode 100644 index 000000000..b66832e0a --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/cpu.pm @@ -0,0 +1,293 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::cpu; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my %metrics_mapping = ( + 'cpu_limit' => { + 'output' => 'CPU Limit', + 'label' => 'cpu-limit', + 'nlabel' => 'elasticpool.cpu.limit.count', + 'unit' => '' + }, + 'cpu_percent' => { + 'output' => 'CPU Percentage Usage', + 'label' => 'cpu-percent', + 'nlabel' => 'elasticpool.cpu.usage.percentage', + 'unit' => '%' + }, + 'cpu_used' => { + 'output' => 'CPU Used', + 'label' => 'cpu-used', + 'nlabel' => 'elasticpool.cpu.used.count', + 'unit' => '' + } +); + +sub custom_metric_calc { + my ($self, %options) = @_; + + $self->{result_values}->{timeframe} = $options{new_datas}->{$self->{instance} . '_timeframe'}; + $self->{result_values}->{value} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{metric}}; + $self->{result_values}->{metric} = $options{extra_options}->{metric}; + return 0; +} + +sub custom_metric_threshold { + my ($self, %options) = @_; + + my $exit = $self->{perfdata}->threshold_check( + value => $self->{result_values}->{value}, + threshold => [ + { label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label} , exit_litteral => 'critical' }, + { label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}, exit_litteral => 'warning' } + ] + ); + return $exit; +} + +sub custom_metric_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + instances => $self->{instance}, + nlabel => $metrics_mapping{$self->{result_values}->{metric}}->{nlabel}, + unit => $metrics_mapping{$self->{result_values}->{metric}}->{unit}, + value => sprintf('%.2f', $self->{result_values}->{value}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}) + ); +} + +sub custom_metric_output { + my ($self, %options) = @_; + + my ($value, $unit) = ($self->{result_values}->{value}, $metrics_mapping{$self->{result_values}->{metric}}->{unit}); + + return sprintf('%s: %.2f %s', $metrics_mapping{$self->{result_values}->{metric}}->{output}, $value, $unit); +} + +sub prefix_metric_output { + my ($self, %options) = @_; + + return "Elastic Pool '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_statistics_output { + my ($self, %options) = @_; + + return "Statistic '" . $options{instance_value}->{display} . "' Metrics "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking Pool'" . $options{instance_value}->{display} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 3, cb_prefix_output => 'prefix_metric_output', cb_long_output => 'long_output', + message_multiple => 'All CPU metrics are ok', indent_long_output => ' ', + group => [ + { name => 'statistics', display_long => 1, cb_prefix_output => 'prefix_statistics_output', + message_multiple => 'All metrics are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + foreach my $metric (keys %metrics_mapping) { + my $entry = { + label => $metrics_mapping{$metric}->{label}, + nlabel => $metrics_mapping{$metric}->{nlabel}, + set => { + key_values => [ { name => $metric }, { name => 'timeframe' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_metric_calc'), + closure_custom_calc_extra_options => { metric => $metric }, + closure_custom_output => $self->can('custom_metric_output'), + closure_custom_perfdata => $self->can('custom_metric_perfdata'), + closure_custom_threshold_check => $self->can('custom_metric_threshold') + } + }; + push @{$self->{maps_counters}->{statistics}}, $entry; + } +} + + +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 => { + 'resource:s@' => { name => 'resource' }, + 'resource-group:s' => { name => 'resource_group' }, + 'filter-metric:s' => { name => 'filter_metric' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{resource}) || $self->{option_results}->{resource} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify either --resource with --resource-group option or --resource .'); + $self->{output}->option_exit(); + } + + $self->{az_resource} = $self->{option_results}->{resource}; + $self->{az_resource_group} = $self->{option_results}->{resource_group} if (defined($self->{option_results}->{resource_group})); + $self->{az_resource_type} = 'servers'; + $self->{az_resource_namespace} = 'Microsoft.Sql'; + $self->{az_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; + $self->{az_interval} = defined($self->{option_results}->{interval}) ? $self->{option_results}->{interval} : 'PT5M'; + $self->{az_aggregations} = ['Average']; + if (defined($self->{option_results}->{aggregation})) { + $self->{az_aggregations} = []; + foreach my $stat (@{$self->{option_results}->{aggregation}}) { + if ($stat ne '') { + push @{$self->{az_aggregations}}, ucfirst(lc($stat)); + } + } + } + + foreach my $metric (keys %metrics_mapping) { + next if (defined($self->{option_results}->{filter_metric}) && $self->{option_results}->{filter_metric} ne '' + && $metric !~ /$self->{option_results}->{filter_metric}/); + + push @{$self->{az_metrics}}, $metric; + } + +} + +sub manage_selection { + my ($self, %options) = @_; + + my %metric_results; + my $raw_results; + foreach my $resource (@{$self->{az_resource}}) { + my $resource_group = $self->{az_resource_group}; + my ($resource_display, $resource_name); + + if ($resource =~ /^(.*)\/elasticpools\/(.*)/) { + ($resource_display, $resource_name) = ($1 . '/' . $2, $resource); + } else { + $self->{output}->add_option_msg(short_msg => 'Incorrect resource format'); + $self->{output}->option_exit(); + }; + + if ($resource =~ /^\/subscriptions\/.*\/resourceGroups\/(.*)\/providers\/Microsoft\.Sql\/servers\/(.*)\/elasticpools\/(.*)$/) { + $resource_group = $1; + $resource_display = $2 . '/' . $3; + $resource_name = $2 . '/elasticpools/' . $3; + } + + ($metric_results{$resource_display}, $raw_results) = $options{custom}->azure_get_metrics( + resource => $resource_name, + resource_group => $resource_group, + resource_type => $self->{az_resource_type}, + resource_namespace => $self->{az_resource_namespace}, + metrics => $self->{az_metrics}, + aggregations => $self->{az_aggregations}, + timeframe => $self->{az_timeframe}, + interval => $self->{az_interval} + ); + + foreach my $metric (@{$self->{az_metrics}}) { + foreach my $aggregation (@{$self->{az_aggregations}}) { + next if (!defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) && !defined($self->{option_results}->{zeroed})); + + $self->{metrics}->{$resource_display}->{display} = $resource_display; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{display} = lc($aggregation); + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{timeframe} = $self->{az_timeframe}; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{$metric} = + defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) ? + $metric_results{$resource_display}->{$metric}->{lc($aggregation)} : 0; + } + } + } + + if (scalar(keys %{$self->{metrics}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values'); + $self->{output}->option_exit(); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Azure SQL Elastic Pool CPU metrics. +(Only applies to vCore-based elastic pools) + +Example: + +Using resource name : + +perl centreon_plugins.pl --plugin=cloud::azure::database::elasticpool::plugin --custommode=azcli --mode=cpu +--resource=/elasticpools/ --resource-group= --aggregation='average' +--critical-cpu-percent='90' --verbose + +Using resource id : + +perl centreon_plugins.pl --plugin=cloud::azure::compute::virtualmachine::plugin --custommode=azcli --mode=cpu +--resource='/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Sql/servers/xxx/elasticpools/xxx' +--aggregation='average' --critical-cpu-percent='90' --verbose + +Default aggregation: 'average' / 'minimum' and 'maximum' are valid. + +=over 8 + +=item B<--resource> + +Set resource name or id (Required). + +=item B<--resource-group> + +Set resource group (Required if resource's name is used). + +=item B<--filter-metric> + +Filter on specific metrics. The Azure format must be used, for example: 'cpu_percent' +(Can be a regexp). + +=item B<--warning-*> + +Warning threshold where * can be: 'cpu-limit', 'cpu-percent', 'cpu-used'. + +=item B<--critical-*> + +Critical threshold where * can be: 'cpu-limit', 'cpu-percent', 'cpu-used'. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/discovery.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/discovery.pm new file mode 100644 index 000000000..c399bcec7 --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/discovery.pm @@ -0,0 +1,60 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::discovery; + +use base qw(cloud::azure::management::monitor::mode::discovery); + +use strict; +use warnings; + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{namespace} = 'Microsoft.Sql'; + $self->{type} = 'servers/elasticpools'; +} + +1; + +__END__ + +=head1 MODE + +Network Interface discovery. + +=over 8 + +=item B<--resource-group> + +Specify resource group. + +=item B<--location> + +Specify location. + +=item B<--prettify> + +Prettify JSON output. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/dtu.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/dtu.pm new file mode 100644 index 000000000..21eab95df --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/dtu.pm @@ -0,0 +1,293 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::dtu; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my %metrics_mapping = ( + 'dtu_consumption_percent' => { + 'output' => 'DTU consumption percentage', + 'label' => 'dtu-consumption-percent', + 'nlabel' => 'elasticpool.dtu.consumpution.percentage', + 'unit' => '%' + }, + 'eDTU_limit' => { + 'output' => 'eDTU Limit', + 'label' => 'edtu-limit', + 'nlabel' => 'elasticpool.edtu.limit.count', + 'unit' => '' + }, + 'eDTU_used' => { + 'output' => 'eDTU Used', + 'label' => 'edtu-used', + 'nlabel' => 'elasticpool.edtu.used.count', + 'unit' => '' + } +); + +sub custom_metric_calc { + my ($self, %options) = @_; + + $self->{result_values}->{timeframe} = $options{new_datas}->{$self->{instance} . '_timeframe'}; + $self->{result_values}->{value} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{metric}}; + $self->{result_values}->{metric} = $options{extra_options}->{metric}; + return 0; +} + +sub custom_metric_threshold { + my ($self, %options) = @_; + + my $exit = $self->{perfdata}->threshold_check( + value => $self->{result_values}->{value}, + threshold => [ + { label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label} , exit_litteral => 'critical' }, + { label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}, exit_litteral => 'warning' } + ] + ); + return $exit; +} + +sub custom_metric_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + instances => $self->{instance}, + nlabel => $metrics_mapping{$self->{result_values}->{metric}}->{nlabel}, + unit => $metrics_mapping{$self->{result_values}->{metric}}->{unit}, + value => sprintf('%.2f', $self->{result_values}->{value}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}) + ); +} + +sub custom_metric_output { + my ($self, %options) = @_; + + my ($value, $unit) = ($self->{result_values}->{value}, $metrics_mapping{$self->{result_values}->{metric}}->{unit}); + + return sprintf('%s: %.2f %s', $metrics_mapping{$self->{result_values}->{metric}}->{output}, $value, $unit); +} + +sub prefix_metric_output { + my ($self, %options) = @_; + + return "Elastic Pool '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_statistics_output { + my ($self, %options) = @_; + + return "Statistic '" . $options{instance_value}->{display} . "' Metrics "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking Pool'" . $options{instance_value}->{display} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 3, cb_prefix_output => 'prefix_metric_output', cb_long_output => 'long_output', + message_multiple => 'All CPU metrics are ok', indent_long_output => ' ', + group => [ + { name => 'statistics', display_long => 1, cb_prefix_output => 'prefix_statistics_output', + message_multiple => 'All metrics are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + foreach my $metric (keys %metrics_mapping) { + my $entry = { + label => $metrics_mapping{$metric}->{label}, + nlabel => $metrics_mapping{$metric}->{nlabel}, + set => { + key_values => [ { name => $metric }, { name => 'timeframe' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_metric_calc'), + closure_custom_calc_extra_options => { metric => $metric }, + closure_custom_output => $self->can('custom_metric_output'), + closure_custom_perfdata => $self->can('custom_metric_perfdata'), + closure_custom_threshold_check => $self->can('custom_metric_threshold') + } + }; + push @{$self->{maps_counters}->{statistics}}, $entry; + } +} + + +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 => { + 'resource:s@' => { name => 'resource' }, + 'resource-group:s' => { name => 'resource_group' }, + 'filter-metric:s' => { name => 'filter_metric' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{resource}) || $self->{option_results}->{resource} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify either --resource with --resource-group option or --resource .'); + $self->{output}->option_exit(); + } + + $self->{az_resource} = $self->{option_results}->{resource}; + $self->{az_resource_group} = $self->{option_results}->{resource_group} if (defined($self->{option_results}->{resource_group})); + $self->{az_resource_type} = 'servers'; + $self->{az_resource_namespace} = 'Microsoft.Sql'; + $self->{az_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; + $self->{az_interval} = defined($self->{option_results}->{interval}) ? $self->{option_results}->{interval} : 'PT5M'; + $self->{az_aggregations} = ['Average']; + if (defined($self->{option_results}->{aggregation})) { + $self->{az_aggregations} = []; + foreach my $stat (@{$self->{option_results}->{aggregation}}) { + if ($stat ne '') { + push @{$self->{az_aggregations}}, ucfirst(lc($stat)); + } + } + } + + foreach my $metric (keys %metrics_mapping) { + next if (defined($self->{option_results}->{filter_metric}) && $self->{option_results}->{filter_metric} ne '' + && $metric !~ /$self->{option_results}->{filter_metric}/); + + push @{$self->{az_metrics}}, $metric; + } + +} + +sub manage_selection { + my ($self, %options) = @_; + + my %metric_results; + my $raw_results; + foreach my $resource (@{$self->{az_resource}}) { + my $resource_group = $self->{az_resource_group}; + my ($resource_display, $resource_name); + + if ($resource =~ /^(.*)\/elasticpools\/(.*)/) { + ($resource_display, $resource_name) = ($1 . '/' . $2, $resource); + } else { + $self->{output}->add_option_msg(short_msg => 'Incorrect resource format'); + $self->{output}->option_exit(); + }; + + if ($resource =~ /^\/subscriptions\/.*\/resourceGroups\/(.*)\/providers\/Microsoft\.Sql\/servers\/(.*)\/elasticpools\/(.*)$/) { + $resource_group = $1; + $resource_display = $2 . '/' . $3; + $resource_name = $2 . '/elasticpools/' . $3; + } + + ($metric_results{$resource_display}, $raw_results) = $options{custom}->azure_get_metrics( + resource => $resource_name, + resource_group => $resource_group, + resource_type => $self->{az_resource_type}, + resource_namespace => $self->{az_resource_namespace}, + metrics => $self->{az_metrics}, + aggregations => $self->{az_aggregations}, + timeframe => $self->{az_timeframe}, + interval => $self->{az_interval} + ); + + foreach my $metric (@{$self->{az_metrics}}) { + foreach my $aggregation (@{$self->{az_aggregations}}) { + next if (!defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) && !defined($self->{option_results}->{zeroed})); + + $self->{metrics}->{$resource_display}->{display} = $resource_display; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{display} = lc($aggregation); + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{timeframe} = $self->{az_timeframe}; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{$metric} = + defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) ? + $metric_results{$resource_display}->{$metric}->{lc($aggregation)} : 0; + } + } + } + + if (scalar(keys %{$self->{metrics}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values'); + $self->{output}->option_exit(); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Azure SQL Elastic Pool DTU metrics. +(Only applies to DTU-based elastic pools) + +Example: + +Using resource name : + +perl centreon_plugins.pl --plugin=cloud::azure::database::elasticpool::plugin --custommode=azcli --mode=dtu +--resource=/elasticpools/ --resource-group= --aggregation='average' +--critical-dtu-consumption-percent='90' --verbose + +Using resource id : + +perl centreon_plugins.pl --plugin=cloud::azure::compute::virtualmachine::plugin --custommode=azcli --mode=dtu +--resource='/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Sql/servers/xxx/elasticpools/xxx' +--aggregation='average' --critical-dtu-consumption-percent='90' --verbose + +Default aggregation: 'average' / 'minimum' and 'maximum' are valid. + +=over 8 + +=item B<--resource> + +Set resource name or id (Required). + +=item B<--resource-group> + +Set resource group (Required if resource's name is used). + +=item B<--filter-metric> + +Filter on specific metrics. The Azure format must be used, for example: 'dtu_consumption_percent' +(Can be a regexp). + +=item B<--warning-*> + +Warning threshold where * can be: 'dtu-consumption-percent', 'edtu-limit', 'edtu-used'. + +=item B<--critical-*> + +Critical threshold where * can be: 'dtu-consumption-percent', 'edtu-limit', 'edtu-used'. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/log.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/log.pm new file mode 100644 index 000000000..640c20ea5 --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/log.pm @@ -0,0 +1,296 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::log; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my %metrics_mapping = ( + 'log_write_percent' => { + 'output' => 'Log write IO percentage', + 'label' => 'log-write-percent', + 'nlabel' => 'elasticpool.log.write.percentage', + 'unit' => '%' + }, + 'tempdb_log_size' => { + 'output' => 'Tempdb Log file size', + 'label' => 'tempdb-log-size', + 'nlabel' => 'elasticpool.log.tempdb.size.kilobytes', + 'unit' => 'KB' + }, + 'tempdb_log_used_percent' => { + 'output' => 'Tempdb log usage percentage', + 'label' => 'tempdb-log-used-percent', + 'nlabel' => 'elasticpool.log.tempdb.usage.percentage', + 'unit' => '%' + } +); + +sub custom_metric_calc { + my ($self, %options) = @_; + + $self->{result_values}->{timeframe} = $options{new_datas}->{$self->{instance} . '_timeframe'}; + $self->{result_values}->{value} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{metric}}; + $self->{result_values}->{metric} = $options{extra_options}->{metric}; + return 0; +} + +sub custom_metric_threshold { + my ($self, %options) = @_; + + my $exit = $self->{perfdata}->threshold_check( + value => $self->{result_values}->{value}, + threshold => [ + { label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label} , exit_litteral => 'critical' }, + { label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}, exit_litteral => 'warning' } + ] + ); + return $exit; +} + +sub custom_metric_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + instances => $self->{instance}, + nlabel => $metrics_mapping{$self->{result_values}->{metric}}->{nlabel}, + unit => $metrics_mapping{$self->{result_values}->{metric}}->{unit}, + value => sprintf('%.2f', $self->{result_values}->{value}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}) + ); +} + +sub custom_metric_output { + my ($self, %options) = @_; + + my ($value, $unit) = ($metrics_mapping{$self->{result_values}->{metric}}->{unit} eq 'KB') ? + $self->{perfdata}->change_bytes(value => $self->{result_values}->{value} * 1000) : + ($self->{result_values}->{value}, $metrics_mapping{$self->{result_values}->{metric}}->{unit}); + + return sprintf('%s: %.2f %s', $metrics_mapping{$self->{result_values}->{metric}}->{output}, $value, $unit); +} + +sub prefix_metric_output { + my ($self, %options) = @_; + + return "Elastic Pool '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_statistics_output { + my ($self, %options) = @_; + + return "Statistic '" . $options{instance_value}->{display} . "' Metrics "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking Pool'" . $options{instance_value}->{display} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 3, cb_prefix_output => 'prefix_metric_output', cb_long_output => 'long_output', + message_multiple => 'All CPU metrics are ok', indent_long_output => ' ', + group => [ + { name => 'statistics', display_long => 1, cb_prefix_output => 'prefix_statistics_output', + message_multiple => 'All metrics are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + foreach my $metric (keys %metrics_mapping) { + my $entry = { + label => $metrics_mapping{$metric}->{label}, + nlabel => $metrics_mapping{$metric}->{nlabel}, + set => { + key_values => [ { name => $metric }, { name => 'timeframe' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_metric_calc'), + closure_custom_calc_extra_options => { metric => $metric }, + closure_custom_output => $self->can('custom_metric_output'), + closure_custom_perfdata => $self->can('custom_metric_perfdata'), + closure_custom_threshold_check => $self->can('custom_metric_threshold') + } + }; + push @{$self->{maps_counters}->{statistics}}, $entry; + } +} + + +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 => { + 'resource:s@' => { name => 'resource' }, + 'resource-group:s' => { name => 'resource_group' }, + 'filter-metric:s' => { name => 'filter_metric' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{resource}) || $self->{option_results}->{resource} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify either --resource with --resource-group option or --resource .'); + $self->{output}->option_exit(); + } + + $self->{az_resource} = $self->{option_results}->{resource}; + $self->{az_resource_group} = $self->{option_results}->{resource_group} if (defined($self->{option_results}->{resource_group})); + $self->{az_resource_type} = 'servers'; + $self->{az_resource_namespace} = 'Microsoft.Sql'; + $self->{az_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; + $self->{az_interval} = defined($self->{option_results}->{interval}) ? $self->{option_results}->{interval} : 'PT5M'; + $self->{az_aggregations} = ['Average', 'Maximum']; + if (defined($self->{option_results}->{aggregation})) { + $self->{az_aggregations} = []; + foreach my $stat (@{$self->{option_results}->{aggregation}}) { + if ($stat ne '') { + push @{$self->{az_aggregations}}, ucfirst(lc($stat)); + } + } + } + + foreach my $metric (keys %metrics_mapping) { + next if (defined($self->{option_results}->{filter_metric}) && $self->{option_results}->{filter_metric} ne '' + && $metric !~ /$self->{option_results}->{filter_metric}/); + + push @{$self->{az_metrics}}, $metric; + } + +} + +sub manage_selection { + my ($self, %options) = @_; + + my %metric_results; + my $raw_results; + foreach my $resource (@{$self->{az_resource}}) { + my $resource_group = $self->{az_resource_group}; + my ($resource_display, $resource_name); + + if ($resource =~ /^(.*)\/elasticpools\/(.*)/) { + ($resource_display, $resource_name) = ($1 . '/' . $2, $resource); + } else { + $self->{output}->add_option_msg(short_msg => 'Incorrect resource format'); + $self->{output}->option_exit(); + }; + + if ($resource =~ /^\/subscriptions\/.*\/resourceGroups\/(.*)\/providers\/Microsoft\.Sql\/servers\/(.*)\/elasticpools\/(.*)$/) { + $resource_group = $1; + $resource_display = $2 . '/' . $3; + $resource_name = $2 . '/elasticpools/' . $3; + } + + ($metric_results{$resource_display}, $raw_results) = $options{custom}->azure_get_metrics( + resource => $resource_name, + resource_group => $resource_group, + resource_type => $self->{az_resource_type}, + resource_namespace => $self->{az_resource_namespace}, + metrics => $self->{az_metrics}, + aggregations => $self->{az_aggregations}, + timeframe => $self->{az_timeframe}, + interval => $self->{az_interval} + ); + + foreach my $metric (@{$self->{az_metrics}}) { + foreach my $aggregation (@{$self->{az_aggregations}}) { + next if (!defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) && !defined($self->{option_results}->{zeroed})); + + $self->{metrics}->{$resource_display}->{display} = $resource_display; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{display} = lc($aggregation); + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{timeframe} = $self->{az_timeframe}; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{$metric} = + defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) ? + $metric_results{$resource_display}->{$metric}->{lc($aggregation)} : 0; + } + } + } + + if (scalar(keys %{$self->{metrics}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values'); + $self->{output}->option_exit(); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Azure SQL Elastic Pool Storage metrics. + +Example: + +Using resource name : + +perl centreon_plugins.pl --plugin=cloud::azure::database::elasticpool::plugin --custommode=azcli --mode=storage +--resource=/elasticpools/ --resource-group= --aggregation='average' +--allocated-data-storage-percent='90' --verbose + +Using resource id : + +perl centreon_plugins.pl --plugin=cloud::azure::compute::virtualmachine::plugin --custommode=azcli --mode=sessions +--resource='/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Sql/servers/xxx/elasticpools/xxx' +--aggregation='average' --allocated-data-storage-percent='90' --verbose + +Default aggregation: 'average' / 'minimum' and 'maximum' are valid. + +=over 8 + +=item B<--resource> + +Set resource name or id (Required). + +=item B<--resource-group> + +Set resource group (Required if resource's name is used). + +=item B<--filter-metric> + +Filter on specific metrics. The Azure format must be used, for example: 'allocated_data_storage_percent' +(Can be a regexp). + +=item B<--warning-*> + +Warning threshold where * can be: 'allocated-data-storage', allocated-data-storage-percent', +'storage-percent', 'storage-used'. + +=item B<--critical-*> + +Critical threshold where * can be: 'allocated-data-storage', allocated-data-storage-percent', +'storage-percent', 'storage-used'. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/sessions.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/sessions.pm new file mode 100644 index 000000000..4f868ea97 --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/sessions.pm @@ -0,0 +1,286 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::sessions; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my %metrics_mapping = ( + 'sessions_percent' => { + 'output' => 'Sessions usage percentage', + 'label' => 'sessions-percent', + 'nlabel' => 'elasticpool.sessions.usage.percentage', + 'unit' => '%' + }, + 'workers_percent' => { + 'output' => 'Workers usage percentage', + 'label' => 'workers-percent', + 'nlabel' => 'elasticpool.workers.usage.percentage', + 'unit' => '%' + } +); + +sub custom_metric_calc { + my ($self, %options) = @_; + + $self->{result_values}->{timeframe} = $options{new_datas}->{$self->{instance} . '_timeframe'}; + $self->{result_values}->{value} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{metric}}; + $self->{result_values}->{metric} = $options{extra_options}->{metric}; + return 0; +} + +sub custom_metric_threshold { + my ($self, %options) = @_; + + my $exit = $self->{perfdata}->threshold_check( + value => $self->{result_values}->{value}, + threshold => [ + { label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label} , exit_litteral => 'critical' }, + { label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}, exit_litteral => 'warning' } + ] + ); + return $exit; +} + +sub custom_metric_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + instances => $self->{instance}, + nlabel => $metrics_mapping{$self->{result_values}->{metric}}->{nlabel}, + unit => $metrics_mapping{$self->{result_values}->{metric}}->{unit}, + value => sprintf('%.2f', $self->{result_values}->{value}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}) + ); +} + +sub custom_metric_output { + my ($self, %options) = @_; + + my ($value, $unit) = ($self->{result_values}->{value}, $metrics_mapping{$self->{result_values}->{metric}}->{unit}); + + return sprintf('%s: %.2f %s', $metrics_mapping{$self->{result_values}->{metric}}->{output}, $value, $unit); +} + +sub prefix_metric_output { + my ($self, %options) = @_; + + return "Elastic Pool '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_statistics_output { + my ($self, %options) = @_; + + return "Statistic '" . $options{instance_value}->{display} . "' Metrics "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking Pool'" . $options{instance_value}->{display} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 3, cb_prefix_output => 'prefix_metric_output', cb_long_output => 'long_output', + message_multiple => 'All CPU metrics are ok', indent_long_output => ' ', + group => [ + { name => 'statistics', display_long => 1, cb_prefix_output => 'prefix_statistics_output', + message_multiple => 'All metrics are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + foreach my $metric (keys %metrics_mapping) { + my $entry = { + label => $metrics_mapping{$metric}->{label}, + nlabel => $metrics_mapping{$metric}->{nlabel}, + set => { + key_values => [ { name => $metric }, { name => 'timeframe' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_metric_calc'), + closure_custom_calc_extra_options => { metric => $metric }, + closure_custom_output => $self->can('custom_metric_output'), + closure_custom_perfdata => $self->can('custom_metric_perfdata'), + closure_custom_threshold_check => $self->can('custom_metric_threshold') + } + }; + push @{$self->{maps_counters}->{statistics}}, $entry; + } +} + + +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 => { + 'resource:s@' => { name => 'resource' }, + 'resource-group:s' => { name => 'resource_group' }, + 'filter-metric:s' => { name => 'filter_metric' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{resource}) || $self->{option_results}->{resource} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify either --resource with --resource-group option or --resource .'); + $self->{output}->option_exit(); + } + + $self->{az_resource} = $self->{option_results}->{resource}; + $self->{az_resource_group} = $self->{option_results}->{resource_group} if (defined($self->{option_results}->{resource_group})); + $self->{az_resource_type} = 'servers'; + $self->{az_resource_namespace} = 'Microsoft.Sql'; + $self->{az_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; + $self->{az_interval} = defined($self->{option_results}->{interval}) ? $self->{option_results}->{interval} : 'PT5M'; + $self->{az_aggregations} = ['Average']; + if (defined($self->{option_results}->{aggregation})) { + $self->{az_aggregations} = []; + foreach my $stat (@{$self->{option_results}->{aggregation}}) { + if ($stat ne '') { + push @{$self->{az_aggregations}}, ucfirst(lc($stat)); + } + } + } + + foreach my $metric (keys %metrics_mapping) { + next if (defined($self->{option_results}->{filter_metric}) && $self->{option_results}->{filter_metric} ne '' + && $metric !~ /$self->{option_results}->{filter_metric}/); + + push @{$self->{az_metrics}}, $metric; + } + +} + +sub manage_selection { + my ($self, %options) = @_; + + my %metric_results; + my $raw_results; + foreach my $resource (@{$self->{az_resource}}) { + my $resource_group = $self->{az_resource_group}; + my ($resource_display, $resource_name); + + if ($resource =~ /^(.*)\/elasticpools\/(.*)/) { + ($resource_display, $resource_name) = ($1 . '/' . $2, $resource); + } else { + $self->{output}->add_option_msg(short_msg => 'Incorrect resource format'); + $self->{output}->option_exit(); + }; + + if ($resource =~ /^\/subscriptions\/.*\/resourceGroups\/(.*)\/providers\/Microsoft\.Sql\/servers\/(.*)\/elasticpools\/(.*)$/) { + $resource_group = $1; + $resource_display = $2 . '/' . $3; + $resource_name = $2 . '/elasticpools/' . $3; + } + + ($metric_results{$resource_display}, $raw_results) = $options{custom}->azure_get_metrics( + resource => $resource_name, + resource_group => $resource_group, + resource_type => $self->{az_resource_type}, + resource_namespace => $self->{az_resource_namespace}, + metrics => $self->{az_metrics}, + aggregations => $self->{az_aggregations}, + timeframe => $self->{az_timeframe}, + interval => $self->{az_interval} + ); + + foreach my $metric (@{$self->{az_metrics}}) { + foreach my $aggregation (@{$self->{az_aggregations}}) { + next if (!defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) && !defined($self->{option_results}->{zeroed})); + + $self->{metrics}->{$resource_display}->{display} = $resource_display; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{display} = lc($aggregation); + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{timeframe} = $self->{az_timeframe}; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{$metric} = + defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) ? + $metric_results{$resource_display}->{$metric}->{lc($aggregation)} : 0; + } + } + } + + if (scalar(keys %{$self->{metrics}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values'); + $self->{output}->option_exit(); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Azure SQL Elastic Pool Sessions metrics. + +Example: + +Using resource name : + +perl centreon_plugins.pl --plugin=cloud::azure::database::elasticpool::plugin --custommode=azcli --mode=sessions +--resource=/elasticpools/ --resource-group= --aggregation='average' +--critical-sessions-percent='90' --verbose + +Using resource id : + +perl centreon_plugins.pl --plugin=cloud::azure::compute::virtualmachine::plugin --custommode=azcli --mode=sessions +--resource='/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Sql/servers/xxx/elasticpools/xxx' +--aggregation='average' --critical-sessions-percent='90' --verbose + +Default aggregation: 'average' / 'minimum' and 'maximum' are valid. + +=over 8 + +=item B<--resource> + +Set resource name or id (Required). + +=item B<--resource-group> + +Set resource group (Required if resource's name is used). + +=item B<--filter-metric> + +Filter on specific metrics. The Azure format must be used, for example: 'sessions_percent' +(Can be a regexp). + +=item B<--warning-*> + +Warning threshold where * can be: 'workers-percent', sessions_percent'. + +=item B<--critical-*> + +Critical threshold where * can be: 'workers-percent', sessions_percent'. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/mode/storage.pm b/centreon-plugins/cloud/azure/database/elasticpool/mode/storage.pm new file mode 100644 index 000000000..10d3f700c --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/mode/storage.pm @@ -0,0 +1,302 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::mode::storage; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +my %metrics_mapping = ( + 'allocated_data_storage_percent' => { + 'output' => 'Allocated data storage percentage', + 'label' => 'allocated-data-storage-percent', + 'nlabel' => 'elasticpool.storage.data.allocated.percentage', + 'unit' => '%' + }, + 'allocated_data_storage' => { + 'output' => 'Allocated data storage', + 'label' => 'allocated-data-storage', + 'nlabel' => 'elasticpool.storage.data.allocated.bytes', + 'unit' => 'B' + }, + 'storage_percent' => { + 'output' => 'Storage usage percentage', + 'label' => 'storage-percent', + 'nlabel' => 'elasticpool.storage.usage.percentage', + 'unit' => '%' + }, + 'storage_used' => { + 'output' => 'Storage usage', + 'label' => 'storage-used', + 'nlabel' => 'elasticpool.storage.used.bytes', + 'unit' => 'B' + } +); + +sub custom_metric_calc { + my ($self, %options) = @_; + + $self->{result_values}->{timeframe} = $options{new_datas}->{$self->{instance} . '_timeframe'}; + $self->{result_values}->{value} = $options{new_datas}->{$self->{instance} . '_' . $options{extra_options}->{metric}}; + $self->{result_values}->{metric} = $options{extra_options}->{metric}; + return 0; +} + +sub custom_metric_threshold { + my ($self, %options) = @_; + + my $exit = $self->{perfdata}->threshold_check( + value => $self->{result_values}->{value}, + threshold => [ + { label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label} , exit_litteral => 'critical' }, + { label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}, exit_litteral => 'warning' } + ] + ); + return $exit; +} + +sub custom_metric_perfdata { + my ($self, %options) = @_; + + $self->{output}->perfdata_add( + instances => $self->{instance}, + nlabel => $metrics_mapping{$self->{result_values}->{metric}}->{nlabel}, + unit => $metrics_mapping{$self->{result_values}->{metric}}->{unit}, + value => sprintf('%.2f', $self->{result_values}->{value}), + warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}), + critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $metrics_mapping{$self->{result_values}->{metric}}->{label}) + ); +} + +sub custom_metric_output { + my ($self, %options) = @_; + + my ($value, $unit) = ($metrics_mapping{$self->{result_values}->{metric}}->{unit} eq 'B') ? + $self->{perfdata}->change_bytes(value => $self->{result_values}->{value}) : + ($self->{result_values}->{value}, $metrics_mapping{$self->{result_values}->{metric}}->{unit}); + + return sprintf('%s: %.2f %s', $metrics_mapping{$self->{result_values}->{metric}}->{output}, $value, $unit); +} + +sub prefix_metric_output { + my ($self, %options) = @_; + + return "Elastic Pool '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_statistics_output { + my ($self, %options) = @_; + + return "Statistic '" . $options{instance_value}->{display} . "' Metrics "; +} + +sub long_output { + my ($self, %options) = @_; + + return "Checking Pool'" . $options{instance_value}->{display} . "' "; +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 3, cb_prefix_output => 'prefix_metric_output', cb_long_output => 'long_output', + message_multiple => 'All CPU metrics are ok', indent_long_output => ' ', + group => [ + { name => 'statistics', display_long => 1, cb_prefix_output => 'prefix_statistics_output', + message_multiple => 'All metrics are ok', type => 1, skipped_code => { -10 => 1 } } + ] + } + ]; + + foreach my $metric (keys %metrics_mapping) { + my $entry = { + label => $metrics_mapping{$metric}->{label}, + nlabel => $metrics_mapping{$metric}->{nlabel}, + set => { + key_values => [ { name => $metric }, { name => 'timeframe' }, { name => 'display' } ], + closure_custom_calc => $self->can('custom_metric_calc'), + closure_custom_calc_extra_options => { metric => $metric }, + closure_custom_output => $self->can('custom_metric_output'), + closure_custom_perfdata => $self->can('custom_metric_perfdata'), + closure_custom_threshold_check => $self->can('custom_metric_threshold') + } + }; + push @{$self->{maps_counters}->{statistics}}, $entry; + } +} + + +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 => { + 'resource:s@' => { name => 'resource' }, + 'resource-group:s' => { name => 'resource_group' }, + 'filter-metric:s' => { name => 'filter_metric' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + if (!defined($self->{option_results}->{resource}) || $self->{option_results}->{resource} eq '') { + $self->{output}->add_option_msg(short_msg => 'Need to specify either --resource with --resource-group option or --resource .'); + $self->{output}->option_exit(); + } + + $self->{az_resource} = $self->{option_results}->{resource}; + $self->{az_resource_group} = $self->{option_results}->{resource_group} if (defined($self->{option_results}->{resource_group})); + $self->{az_resource_type} = 'servers'; + $self->{az_resource_namespace} = 'Microsoft.Sql'; + $self->{az_timeframe} = defined($self->{option_results}->{timeframe}) ? $self->{option_results}->{timeframe} : 900; + $self->{az_interval} = defined($self->{option_results}->{interval}) ? $self->{option_results}->{interval} : 'PT5M'; + $self->{az_aggregations} = ['Average']; + if (defined($self->{option_results}->{aggregation})) { + $self->{az_aggregations} = []; + foreach my $stat (@{$self->{option_results}->{aggregation}}) { + if ($stat ne '') { + push @{$self->{az_aggregations}}, ucfirst(lc($stat)); + } + } + } + + foreach my $metric (keys %metrics_mapping) { + next if (defined($self->{option_results}->{filter_metric}) && $self->{option_results}->{filter_metric} ne '' + && $metric !~ /$self->{option_results}->{filter_metric}/); + + push @{$self->{az_metrics}}, $metric; + } + +} + +sub manage_selection { + my ($self, %options) = @_; + + my %metric_results; + my $raw_results; + foreach my $resource (@{$self->{az_resource}}) { + my $resource_group = $self->{az_resource_group}; + my ($resource_display, $resource_name); + + if ($resource =~ /^(.*)\/elasticpools\/(.*)/) { + ($resource_display, $resource_name) = ($1 . '/' . $2, $resource); + } else { + $self->{output}->add_option_msg(short_msg => 'Incorrect resource format'); + $self->{output}->option_exit(); + }; + + if ($resource =~ /^\/subscriptions\/.*\/resourceGroups\/(.*)\/providers\/Microsoft\.Sql\/servers\/(.*)\/elasticpools\/(.*)$/) { + $resource_group = $1; + $resource_display = $2 . '/' . $3; + $resource_name = $2 . '/elasticpools/' . $3; + } + + ($metric_results{$resource_display}, $raw_results) = $options{custom}->azure_get_metrics( + resource => $resource_name, + resource_group => $resource_group, + resource_type => $self->{az_resource_type}, + resource_namespace => $self->{az_resource_namespace}, + metrics => $self->{az_metrics}, + aggregations => $self->{az_aggregations}, + timeframe => $self->{az_timeframe}, + interval => $self->{az_interval} + ); + + foreach my $metric (@{$self->{az_metrics}}) { + foreach my $aggregation (@{$self->{az_aggregations}}) { + next if (!defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) && !defined($self->{option_results}->{zeroed})); + + $self->{metrics}->{$resource_display}->{display} = $resource_display; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{display} = lc($aggregation); + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{timeframe} = $self->{az_timeframe}; + $self->{metrics}->{$resource_display}->{statistics}->{lc($aggregation)}->{$metric} = + defined($metric_results{$resource_display}->{$metric}->{lc($aggregation)}) ? + $metric_results{$resource_display}->{$metric}->{lc($aggregation)} : 0; + } + } + } + + if (scalar(keys %{$self->{metrics}}) <= 0) { + $self->{output}->add_option_msg(short_msg => 'No metrics. Check your options or use --zeroed option to set 0 on undefined values'); + $self->{output}->option_exit(); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Azure SQL Elastic Pool Storage metrics. + +Example: + +Using resource name : + +perl centreon_plugins.pl --plugin=cloud::azure::database::elasticpool::plugin --custommode=azcli --mode=storage +--resource=/elasticpools/ --resource-group= --aggregation='average' +--allocated-data-storage-percent='90' --verbose + +Using resource id : + +perl centreon_plugins.pl --plugin=cloud::azure::compute::virtualmachine::plugin --custommode=azcli --mode=sessions +--resource='/subscriptions/xxx/resourceGroups/xxx/providers/Microsoft.Sql/servers/xxx/elasticpools/xxx' +--aggregation='average' --allocated-data-storage-percent='90' --verbose + +Default aggregation: 'average' / 'minimum' and 'maximum' are valid. + +=over 8 + +=item B<--resource> + +Set resource name or id (Required). + +=item B<--resource-group> + +Set resource group (Required if resource's name is used). + +=item B<--filter-metric> + +Filter on specific metrics. The Azure format must be used, for example: 'allocated_data_storage_percent' +(Can be a regexp). + +=item B<--warning-*> + +Warning threshold where * can be: 'allocated-data-storage', allocated-data-storage-percent', +'storage-percent', 'storage-used'. + +=item B<--critical-*> + +Critical threshold where * can be: 'allocated-data-storage', allocated-data-storage-percent', +'storage-percent', 'storage-used'. + +=back + +=cut diff --git a/centreon-plugins/cloud/azure/database/elasticpool/plugin.pm b/centreon-plugins/cloud/azure/database/elasticpool/plugin.pm new file mode 100644 index 000000000..4beaed1c7 --- /dev/null +++ b/centreon-plugins/cloud/azure/database/elasticpool/plugin.pm @@ -0,0 +1,65 @@ +# +# Copyright 2020 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 cloud::azure::database::elasticpool::plugin; + +use strict; +use warnings; +use base qw(centreon::plugins::script_custom); + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new( package => __PACKAGE__, %options ); + bless $self, $class; + + $self->{version} = '0.1'; + %{ $self->{modes} } = ( + 'cpu' => 'cloud::azure::database::elasticpool::mode::cpu', + 'discovery' => 'cloud::azure::database::elasticpool::mode::discovery', + 'dtu' => 'cloud::azure::database::elasticpool::mode::dtu', + 'log' => 'cloud::azure::database::elasticpool::mode::log', + 'sessions' => 'cloud::azure::database::elasticpool::mode::sessions', + 'storage' => 'cloud::azure::database::elasticpool::mode::storage' + ); + + $self->{custom_modes}->{azcli} = 'cloud::azure::custom::azcli'; + $self->{custom_modes}->{api} = 'cloud::azure::custom::api'; + return $self; +} + +sub init { + my ($self, %options) = @_; + + $self->{options}->add_options(arguments => { + 'api-version:s' => { name => 'api_version', default => '2018-01-01' }, + }); + + $self->SUPER::init(%options); +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Microsoft Azure SQL Elastic Pools. + +=cut