From 697e842cd378f844457bcb0c81ab5f40ae3f3933 Mon Sep 17 00:00:00 2001 From: garnier-quentin Date: Fri, 17 Jan 2020 10:59:20 +0100 Subject: [PATCH] add hyperledger plugin --- .../hyperledger/exporter/mode/channels.pm | 273 ++++++++++++++++++ .../blockchain/hyperledger/exporter/plugin.pm | 49 ++++ .../monitoring/openmetrics/custom/file.pm | 28 +- .../monitoring/openmetrics/custom/web.pm | 15 +- .../common/monitoring/openmetrics/scrape.pm | 12 +- 5 files changed, 362 insertions(+), 15 deletions(-) create mode 100644 centreon-plugins/blockchain/hyperledger/exporter/mode/channels.pm create mode 100644 centreon-plugins/blockchain/hyperledger/exporter/plugin.pm diff --git a/centreon-plugins/blockchain/hyperledger/exporter/mode/channels.pm b/centreon-plugins/blockchain/hyperledger/exporter/mode/channels.pm new file mode 100644 index 000000000..c14c4678b --- /dev/null +++ b/centreon-plugins/blockchain/hyperledger/exporter/mode/channels.pm @@ -0,0 +1,273 @@ +# +# 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 blockchain::hyperledger::exporter::mode::channels; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::common::monitoring::openmetrics::scrape; +use Digest::MD5 qw(md5_hex); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { + name => 'channel', type => 3, cb_prefix_output => 'prefix_channel_output', cb_long_output => 'channel_long_output', indent_long_output => ' ', message_multiple => 'All channels are ok', + group => [ + { name => 'channel_global', type => 0, message_separator => ' - ', skipped_code => { -10 => 1 } }, + { name => 'channel_gscd', type => 0, cb_prefix_output => 'prefix_gscd_output', skipped_code => { -10 => 1 } }, + { name => 'channel_gpvd', type => 0, cb_prefix_output => 'prefix_gpvd_output', skipped_code => { -10 => 1 } }, + ] + } + ]; + + foreach (( + ['gscd', 'gossip.state.commit', 'gossip_state_commit_duration'], + ['gpvd', 'gossip.privdata.validation', 'gossip_privdata_validation_duration'] + )) { + $self->{maps_counters}->{'channel_' . $_->[0]} = [ + { label => $_->[0] . '-total', nlabel => 'channel.' . $_->[1] . '.total.count', set => { + key_values => [ { name => $_->[2] . '_count', diff => 1 } ], + output_template => '%s (total)', + perfdatas => [ + { value => $_->[2] . '_count_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + } + ]; + + foreach my $label (('0.005', '0.01', '0.025', '0.05', '0.1', '0.25', '0.5', '1', '2.5', '5', '10', '+Inf')) { + my $perf_label = $label; + $perf_label =~ s/\+Inf/infinite/; + push @{$self->{maps_counters}->{'channel_' . $_->[0]}}, + { + label => $_->[0] . '-time-le-' . $perf_label, nlabel => 'channel.' . $_->[1] . '.time.le.' . $perf_label . '.count', set => { + key_values => [ { name => $_->[2] . '_bucket_' . $label, diff => 1 } ], + output_template => '%s (<= ' . $perf_label . ' sec)', + perfdatas => [ + { value => $_->[2] . '_bucket_' . $label . '_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + }; + } + } + + $self->{maps_counters}->{channel_global} = [ + { label => 'ledger-transaction', nlabel => 'channel.ledger.transaction.count', set => { + key_values => [ { name => 'ledger_transaction_count', diff => 1 } ], + output_template => 'number of transactions processed: %s', + perfdatas => [ + { value => 'ledger_transaction_count_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + }, + { label => 'gossip-membership-total-peers-known', nlabel => 'channel.gossip.membership.total.peers.known.count', set => { + key_values => [ { name => 'gossip_membership_total_peers_known' } ], + output_template => 'total known peers: %s', + perfdatas => [ + { value => 'gossip_membership_total_peers_known_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + }, + { label => 'gossip-state-height', nlabel => 'channel.gossip.state.height.count', set => { + key_values => [ { name => 'gossip_state_height' } ], + output_template => 'current ledger height: %s', + perfdatas => [ + { value => 'gossip_state_height_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + }, + { label => 'ledger-blockchain-height', nlabel => 'channel.ledger.blockchain.height.count', set => { + key_values => [ { name => 'ledger_blockchain_height' } ], + output_template => 'height of the chain in blocks: %s', + perfdatas => [ + { value => 'ledger_blockchain_height_absolute', template => '%s', min => 0, + label_extra_instance => 1 }, + ], + } + }, + ]; +} + +sub channel_long_output { + my ($self, %options) = @_; + + return "checking channel '" . $options{instance_value}->{display} . "'"; +} + +sub prefix_channel_output { + my ($self, %options) = @_; + + return "channel '" . $options{instance_value}->{display} . "' "; +} + +sub prefix_gscd_output { + my ($self, %options) = @_; + + return 'time it takes to commit a block: '; +} + +sub prefix_gpvd_output { + my ($self, %options) = @_; + + return 'time it takes to validate a block: '; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, statefile => 1, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'filter-channel:s' => { name => 'filter_channel' }, + }); + + return $self; +} + +sub change_macros { + my ($self, %options) = @_; + + $options{template} =~ s/%\{(.*?)\}/$options{dimensions}->{$1}/g; + if (defined($options{escape})) { + $options{template} =~ s/([\Q$options{escape}\E])/\\$1/g; + } + return $options{template}; +} + +sub search_metric { + my ($self, %options) = @_; + + return if (!defined($options{metrics}->{$options{label}})); + + foreach (@{$options{metrics}->{$options{label}}->{data}}) { + next if (!defined($_->{dimensions}->{$options{dimension}})); + my $dimension = $_->{dimensions}->{$options{dimension}}; + next if (defined($self->{option_results}->{filter_channel}) && $self->{option_results}->{filter_channel} ne '' && + $dimension !~ /$self->{option_results}->{filter_channel}/); + + if (!defined($self->{channel}->{$dimension})) { + $self->{channel}->{$dimension} = { display => $dimension }; + } + $self->{channel}->{$dimension}->{$options{store}} = {} if (!defined($self->{channel}->{$dimension}->{$options{store}})); + my $key = $self->change_macros(template => $options{key}, dimensions => $_->{dimensions}); + $self->{channel}->{$dimension}->{$options{store}}->{$key} = $_->{value}; + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $metrics = centreon::common::monitoring::openmetrics::scrape::parse(%options, strip_chars => "[\"']"); + $self->{channel} = {}; + $self->search_metric( + metrics => $metrics, + label => 'gossip_state_commit_duration_bucket', + dimension => 'channel', + key => 'gossip_state_commit_duration_bucket_%{le}', + store => 'channel_gscd' + ); + $self->search_metric( + metrics => $metrics, + label => 'gossip_state_commit_duration_count', + dimension => 'channel', + key => 'gossip_state_commit_duration_count', + store => 'channel_gscd' + ); + + $self->search_metric( + metrics => $metrics, + label => 'gossip_privdata_validation_duration_bucket', + dimension => 'channel', + key => 'gossip_privdata_validation_duration_bucket_%{le}', + store => 'channel_gpvd' + ); + $self->search_metric( + metrics => $metrics, + label => 'gossip_privdata_validation_duration_count', + dimension => 'channel', + key => 'gossip_privdata_validation_duration_count', + store => 'channel_gpvd' + ); + + $self->search_metric( + metrics => $metrics, + label => 'ledger_transaction_count', + dimension => 'channel', + key => 'ledger_transaction_count', + store => 'channel_global' + ); + $self->search_metric( + metrics => $metrics, + label => 'gossip_membership_total_peers_known', + dimension => 'channel', + key => 'gossip_membership_total_peers_known', + store => 'channel_global' + ); + $self->search_metric( + metrics => $metrics, + label => 'gossip_state_height', + dimension => 'channel', + key => 'gossip_state_height', + store => 'channel_global' + ); + $self->search_metric( + metrics => $metrics, + label => 'ledger_blockchain_height', + dimension => 'channel', + key => 'ledger_blockchain_height', + store => 'channel_global' + ); + + $self->{cache_name} = 'hyperledger_' . $options{custom}->get_uuid() . '_' . $self->{mode} . '_' . + (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all')) . '_' . + (defined($self->{option_results}->{filter_channel}) ? md5_hex($self->{option_results}->{filter_channel}) : md5_hex('all')); + +} + +1; + +__END__ + +=head1 MODE + +Check blockchain system. + +=over 8 + +=item B<--filter-name> + +Filter channel channel (can be a regexp). + +=item B<--warning-*> B<--critical-*> + +Thresholds. Use --list-counters to get available thresholds options. + +=back + +=cut diff --git a/centreon-plugins/blockchain/hyperledger/exporter/plugin.pm b/centreon-plugins/blockchain/hyperledger/exporter/plugin.pm new file mode 100644 index 000000000..aa4872be5 --- /dev/null +++ b/centreon-plugins/blockchain/hyperledger/exporter/plugin.pm @@ -0,0 +1,49 @@ +# +# 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 blockchain::hyperledger::exporter::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}} = ( + 'channels' => 'blockchain::hyperledger::exporter::mode::channels', + ); + $self->{custom_modes}{web} = 'centreon::common::monitoring::openmetrics::custom::web'; + $self->{custom_modes}{file} = 'centreon::common::monitoring::openmetrics::custom::file'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Hyperledger blockchain with prometheus exporter. + +=cut diff --git a/centreon-plugins/centreon/common/monitoring/openmetrics/custom/file.pm b/centreon-plugins/centreon/common/monitoring/openmetrics/custom/file.pm index 18464bde3..099a951d4 100644 --- a/centreon-plugins/centreon/common/monitoring/openmetrics/custom/file.pm +++ b/centreon-plugins/centreon/common/monitoring/openmetrics/custom/file.pm @@ -23,6 +23,7 @@ package centreon::common::monitoring::openmetrics::custom::file; use strict; use warnings; use centreon::plugins::misc; +use Digest::MD5 qw(md5_hex); sub new { my ($class, %options) = @_; @@ -40,15 +41,15 @@ sub new { if (!defined($options{noptions})) { $options{options}->add_options(arguments => { - "hostname:s" => { name => 'hostname' }, - "ssh-option:s@" => { name => 'ssh_option' }, - "ssh-path:s" => { name => 'ssh_path' }, - "ssh-command:s" => { name => 'ssh_command', default => 'ssh' }, - "timeout:s" => { name => 'timeout', default => 10 }, - "sudo" => { name => 'sudo' }, - "command:s" => { name => 'command', default => 'cat' }, - "command-path:s" => { name => 'command_path' }, - "command-options:s" => { name => 'command_options' }, + 'hostname:s' => { name => 'hostname' }, + 'ssh-option:s@' => { name => 'ssh_option' }, + 'ssh-path:s' => { name => 'ssh_path' }, + 'ssh-command:s' => { name => 'ssh_command', default => 'ssh' }, + 'timeout:s' => { name => 'timeout', default => 10 }, + 'sudo' => { name => 'sudo' }, + 'command:s' => { name => 'command', default => 'cat' }, + 'command-path:s' => { name => 'command_path' }, + 'command-options:s' => { name => 'command_options' }, }); } $options{options}->add_help(package => __PACKAGE__, sections => 'FILE OPTIONS', once => 1); @@ -92,6 +93,15 @@ sub check_options { return 0; } +sub get_uuid { + my ($self, %options) = @_; + + return md5_hex( + ((defined($self->{option_results}->{hostname}) && $self->{option_results}->{hostname} ne '') ? $self->{option_results}->{hostname} : 'none') . '_' . + ((defined($self->{option_results}->{command_options}) && $self->{option_results}->{command_options} ne '') ? $self->{option_results}->{command_options} : 'none') + ); +} + sub scrape { my ($self, %options) = @_; diff --git a/centreon-plugins/centreon/common/monitoring/openmetrics/custom/web.pm b/centreon-plugins/centreon/common/monitoring/openmetrics/custom/web.pm index 411020f0f..b98da6c8d 100644 --- a/centreon-plugins/centreon/common/monitoring/openmetrics/custom/web.pm +++ b/centreon-plugins/centreon/common/monitoring/openmetrics/custom/web.pm @@ -23,6 +23,7 @@ package centreon::common::monitoring::openmetrics::custom::web; use strict; use warnings; use centreon::plugins::http; +use Digest::MD5 qw(md5_hex); sub new { my ($class, %options) = @_; @@ -37,7 +38,7 @@ sub new { $options{output}->add_option_msg(short_msg => "Class Custom: Need to specify 'options' argument."); $options{output}->option_exit(); } - + if (!defined($options{noptions})) { $options{options}->add_options(arguments => { 'hostname:s@' => { name => 'hostname' }, @@ -91,7 +92,7 @@ sub check_options { $self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : ''; $self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : ''; $self->{timeout} = (defined($self->{option_results}->{timeout})) ? shift(@{$self->{option_results}->{timeout}}) : 10; - + if (!defined($self->{hostname})) { $self->{output}->add_option_msg(short_msg => "Need to specify hostname option."); $self->{output}->option_exit(); @@ -121,6 +122,15 @@ sub build_options_for_httplib { } } +sub get_uuid { + my ($self, %options) = @_; + + return md5_hex( + ((defined($self->{hostname}) && $self->{hostname} ne '') ? $self->{hostname} : 'none') . '_' . + ((defined($self->{port}) && $self->{port} ne '') ? $self->{port} : 'none') + ); +} + sub settings { my ($self, %options) = @_; @@ -132,7 +142,6 @@ sub scrape { my ($self, %options) = @_; $self->settings(); - return $self->{http}->request(critical_status => '', warning_status => ''); } diff --git a/centreon-plugins/centreon/common/monitoring/openmetrics/scrape.pm b/centreon-plugins/centreon/common/monitoring/openmetrics/scrape.pm index 7983ffddf..1025e5a71 100644 --- a/centreon-plugins/centreon/common/monitoring/openmetrics/scrape.pm +++ b/centreon-plugins/centreon/common/monitoring/openmetrics/scrape.pm @@ -39,14 +39,20 @@ sub parse { $dimensions =~ s/[{}]//g; $dimensions =~ s/"/'/g; - my %dimensions = map { (split /=/) } split /,/, $dimensions; + $dimensions =~ s/$options{strip_chars}//g if (defined($options{strip_chars})); + my %dimensions = (); + foreach (split /,/, $dimensions) { + my ($key, $value) = split /=/; + $dimensions{$key} = $value; + } push @{$result->{metrics}->{$metric}->{data}}, { value => centreon::plugins::misc::expand_exponential(value => $value), dimensions => \%dimensions, - dimensions_string => $dimensions }; + dimensions_string => $dimensions + }; } - + return $result->{metrics}; }