diff --git a/centreon-plugins/blockchain/parity/ethpoller/custom/api.pm b/centreon-plugins/blockchain/parity/ethpoller/custom/api.pm new file mode 100644 index 000000000..b6462c32f --- /dev/null +++ b/centreon-plugins/blockchain/parity/ethpoller/custom/api.pm @@ -0,0 +1,204 @@ +# +# 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::parity::ethpoller::custom::api; + +use strict; +use warnings; +use centreon::plugins::http; +use DateTime; +use JSON::XS; + +sub new { + my ($class, %options) = @_; + my $self = {}; + bless $self, $class; + + 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(); + } + + if (!defined($options{noptions})) { + $options{options}->add_options(arguments => { + "hostname:s" => { name => 'hostname' }, + "port:s" => { name => 'port' }, + "proto:s" => { name => 'proto' }, + "url-path:s" => { name => 'url_path' }, + "timeout:s" => { name => 'timeout' }, + }); + } + $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); + + $self->{output} = $options{output}; + $self->{mode} = $options{mode}; + $self->{http} = centreon::plugins::http->new(%options); + + return $self; +} + +sub set_options { + my ($self, %options) = @_; + + $self->{option_results} = $options{option_results}; +} + +sub set_defaults { + my ($self, %options) = @_; + + foreach (keys %{$options{default}}) { + if ($_ eq $self->{mode}) { + for (my $i = 0; $i < scalar(@{$options{default}->{$_}}); $i++) { + foreach my $opt (keys %{$options{default}->{$_}[$i]}) { + if (!defined($self->{option_results}->{$opt}[$i])) { + $self->{option_results}->{$opt}[$i] = $options{default}->{$_}[$i]->{$opt}; + } + } + } + } + } +} + +sub check_options { + my ($self, %options) = @_; + + $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : undef; + $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 8000; + $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'http'; + $self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : ''; + $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; + + if (!defined($self->{hostname}) || $self->{hostname} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --hostname option."); + $self->{output}->option_exit(); + } + + return 0; +} + +sub build_options_for_httplib { + my ($self, %options) = @_; + + $self->{option_results}->{hostname} = $self->{hostname}; + $self->{option_results}->{timeout} = $self->{timeout}; + $self->{option_results}->{port} = $self->{port}; + $self->{option_results}->{proto} = $self->{proto}; + $self->{option_results}->{url_path} = $self->{url_path}; + $self->{option_results}->{warning_status} = ''; + $self->{option_results}->{critical_status} = ''; +} + +sub settings { + my ($self, %options) = @_; + + $self->build_options_for_httplib(); + $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->set_options(%{$self->{option_results}}); +} + +sub get_connection_info { + my ($self, %options) = @_; + + return $self->{hostname} . ":" . $self->{port}; +} + +sub get_hostname { + my ($self, %options) = @_; + + return $self->{hostname}; +} + +sub get_port { + my ($self, %options) = @_; + + return $self->{port}; +} + +sub request_api { + my ($self, %options) = @_; + + $self->settings; + + $self->{output}->output_add(long_msg => "Query URL: '" . $self->{proto} . "://" . $self->{hostname} . + $self->{url_path} . $options{url_path} . "'", debug => 1); + + my $content = $self->{http}->request(url_path => $self->{url_path} . $options{url_path}); + + my $decoded; + eval { + $decoded = JSON::XS->new->utf8->decode($content); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); + $self->{output}->option_exit(); + } + + return $decoded; +} + +1; + +__END__ + +=head1 NAME + +Parity eth-poller Rest API + +=head1 SYNOPSIS + +Parity eth-poller Rest API custom mode + +=head1 REST API OPTIONS + +Parity eth-poller Rest API + +=over 8 + +=item B<--hostname> + +Parity eth-poller API hostname. + +=item B<--port> + +API port (Default: 8000) + +=item B<--proto> + +Specify https if needed (Default: 'http') + +=item B<--url-path> + +API URL path (Default: '') + +=item B<--timeout> + +Set HTTP timeout + +=back + +=head1 DESCRIPTION + +B. + +=cut \ No newline at end of file diff --git a/centreon-plugins/blockchain/parity/ethpoller/mode/fork.pm b/centreon-plugins/blockchain/parity/ethpoller/mode/fork.pm new file mode 100644 index 000000000..226fdbc4a --- /dev/null +++ b/centreon-plugins/blockchain/parity/ethpoller/mode/fork.pm @@ -0,0 +1,98 @@ +# +# 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::parity::ethpoller::mode::fork; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold catalog_status_calc); + +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 => { + 'unknown-status:s' => { name => 'unknown_status', default => '' }, + 'warning-status:s' => { name => 'warning_status', default => '' }, + 'critical-status:s' => { name => 'critical_status', default => '%{listening} !~ /true/' }, + }); + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->request_api(url_path => '/fork'); + + # use Data::Dumper; + # print Dumper($result); + + # Unix time conversion + my $res_timestamp = localtime(hex($result->{last_update}->{timestamp})); + + # Alerts management + my $cache = Cache::File->new( cache_root => './parity-eth-poller-cache' ); + + if (my $cached_timestamp = $cache->get('fork_timestamp')) { + if ($cached_timestamp ne $res_timestamp) { + #alert + } + } else { + $cache->set('fork_timestamp', $res_timestamp); + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Fork]: fork_timestamp: ' . $res_timestamp . + ' | fork_occurence: ' . $result->{occurence} . ' | fork_blockNumber: ' . $result->{last_update}->{blockNumber} . + ' | fork_in: ' . $result->{last_update}->{in} . ' | fork_out: ' . $result->{last_update}->{out} ); + +} + +1; + +__END__ + +=head1 MODE + +Check Parity eth-poller for forks details + +=over 8 + +=item B<--unknown-status> + +Set unknown threshold for listening status (Default: ''). + +=item B<--warning-status> + +Set warning threshold for listening status (Default: ''). + +=item B<--critical-status> + +Set critical threshold for listening status (Default: '%{is_mining} !~ /true/'). + +=item B<--warning-peers> B<--critical-peers> + +Warning and Critical threhsold on the number of peer + +=back + +=cut diff --git a/centreon-plugins/blockchain/parity/ethpoller/mode/stats.pm b/centreon-plugins/blockchain/parity/ethpoller/mode/stats.pm new file mode 100644 index 000000000..4844e9d6f --- /dev/null +++ b/centreon-plugins/blockchain/parity/ethpoller/mode/stats.pm @@ -0,0 +1,114 @@ +# +# 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::parity::ethpoller::mode::stats; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use Digest::MD5 qw(md5_hex); + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', cb_prefix_output => 'prefix_module_output', type => 0 }, + ]; + + $self->{maps_counters}->{global} = [ + { label => 'stats_blockInterval', nlabel => 'parity.network.peers.count', set => { + key_values => [ { name => 'stats_blockInterval' } ], + output_template => "Block interval: %d ", + perfdatas => [ + { label => 'stats_blockInterval', value => 'stats_blockInterval_absolute', template => '%d', min => 0 } + ], + } + }, + { label => 'stats_contracts', nlabel => 'eth.poller.stats.contracts.number', set => { + key_values => [ { name => 'stats_contracts' } ], + output_template => "Cumulative contracts: %d ", + perfdatas => [ + { label => 'stats_contracts', value => 'stats_contracts_absolute', template => '%d', min => 0 } + ], + } + }, + { label => 'stats_blocks', nlabel => 'eth.poller.stats.blocks.number', set => { + key_values => [ { name => 'stats_blocks' } ], + output_template => "Cumulative blocks: %d ", + perfdatas => [ + { label => 'stats_blocks', value => 'stats_blocks_absolute', template => '%d', min => 0 } + ], + } + }, + { label => 'stats_transactions', nlabel => 'eth.poller.stats.transactions.number', set => { + key_values => [ { name => 'stats_transactions' } ], + output_template => "Cumulative transactions: %d ", + perfdatas => [ + { label => 'stats_transactions', value => 'stats_transactions_absolute', template => '%d', min => 0 } + ], + } + }, + ]; + +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Stats '"; +} + +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-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $result = $options{custom}->request_api(url_path => '/stats'); + + # use Data::Dumper; + # print Dumper($result); + + $self->{global} = { stats_blockInterval => $result->{blockInterval}, + stats_contracts => $result->{cumulative}->{contracts}, + stats_blocks => $result->{cumulative}->{blocks}, + stats_transactions => $result->{cumulative}->{transactions} + }; +} + +1; + +__END__ + +=head1 MODE + +Check Parity eth-poller for accounts tracking + +=cut diff --git a/centreon-plugins/blockchain/parity/ethpoller/mode/watchlist.pm b/centreon-plugins/blockchain/parity/ethpoller/mode/watchlist.pm new file mode 100644 index 000000000..8a6e734f2 --- /dev/null +++ b/centreon-plugins/blockchain/parity/ethpoller/mode/watchlist.pm @@ -0,0 +1,130 @@ +# +# 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::parity::ethpoller::mode::watchlist; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use Digest::MD5 qw(md5_hex); + +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-name:s" => { name => 'filter_name' }, + }); + + return $self; +} + +sub manage_selection { + my ($self, %options) = @_; + + my $results = $options{custom}->request_api(url_path => '/watchlist'); + + # use Data::Dumper; + # print Dumper($results); + + # Alerts management + my $cache = Cache::File->new( cache_root => './parity-eth-poller-cache' ); + + if (my $cached_balance = $cache->get('contract_balance')) { + if ($cached_balance != $contract->{balance}) { + #alert + } + } else { + $cache->set('contract_balance', $contract->{balance}); + } + + foreach my $account (@{$results->{Accounts}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $account->{id} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $account->{id} . "': no matching filter name.", debug => 1); + next; + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Account ' . $account->{id} . ']: label: ' . $account->{label} . ' | nonce: ' . $account->{nonce} . + ' | timestamp: ' . localtime(hex($account->{last_update}->{timestamp})) . ' | blockNumber: ' . $account->{last_update}->{blockNumber} . + ' | receiver: ' . $account->{last_update}->{receiver} . ' | value: ' . $account->{last_update}->{value} ); + } + + foreach my $minner (@{$results->{Miners}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $minner->{id} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $minner->{id} . "': no matching filter name.", debug => 1); + next; + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Minner ' . $minner->{id} . ']: label: ' . $minner->{label} . ' | blocks: ' . $minner->{blocks} . + ' | timestamp: ' . localtime(hex($minner->{last_update}->{timestamp})) . ' | blockNumber: ' . $minner->{last_update}->{blockNumber} ); + } + + foreach my $contract (@{$results->{Constracts}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $contract->{id} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $contract->{id} . "': no matching filter name.", debug => 1); + next; + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Contract ' . $contract->{id} . ']: label: ' . $contract->{label} . ' | balance: ' . $contract->{balance} . + ' | timestamp: ' . localtime(hex($contract->{last_update}->{timestamp})) . ' | blockNumber: ' . $contract->{last_update}->{blockNumber} . + ' | sender: ' . $contract->{last_update}->{sender} . ' | value: ' . $contract->{last_update}->{value} ); + } + + foreach my $function (@{$results->{Functions}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $function->{id} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $function->{id} . "': no matching filter name.", debug => 1); + next; + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Function ' . $function->{id} . ']: label: ' . $function->{label} . ' | calls: ' . $function->{calls} . + ' | timestamp: ' . localtime(hex($function->{last_update}->{timestamp})) . ' | blockNumber: ' . $function->{last_update}->{blockNumber} . + ' | sender: ' . $function->{last_update}->{sender} . ' | receiver: ' . $function->{last_update}->{receiver} . + ' | value: ' . $function->{last_update}->{value} ); + } + + foreach my $event (@{$results->{Events}}) { + if (defined($self->{option_results}->{filter_name}) && $self->{option_results}->{filter_name} ne '' && + $event->{id} !~ /$self->{option_results}->{filter_name}/) { + $self->{output}->output_add(long_msg => "skipping '" . $event->{id} . "': no matching filter name.", debug => 1); + next; + } + + $self->{output}->output_add(severity => 'OK', long_msg => '[Event ' . $event->{id} . ']: label: ' . $event->{label} . ' | calls: ' . $event->{calls} . + ' | timestamp: ' . localtime(hex($event->{last_update}->{timestamp})) . ' | blockNumber: ' . $event->{last_update}->{blockNumber} . + ' | sender: ' . $event->{last_update}->{sender} . ' | receiver: ' . $event->{last_update}->{receiver}); + } + +} + +1; + +__END__ + +=head1 MODE + +Check Parity eth-poller for accounts tracking + +=cut diff --git a/centreon-plugins/blockchain/parity/ethpoller/plugin.pm b/centreon-plugins/blockchain/parity/ethpoller/plugin.pm new file mode 100644 index 000000000..77989acd3 --- /dev/null +++ b/centreon-plugins/blockchain/parity/ethpoller/plugin.pm @@ -0,0 +1,50 @@ +# +# 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::parity::ethpoller::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}} = ( + 'watchlist' => 'blockchain::parity::ethpoller::mode::watchlist', + 'fork' => 'blockchain::parity::ethpoller::mode::fork', + 'stats' => 'blockchain::parity::ethpoller::mode::stats' + ); + $self->{custom_modes}{api} = 'blockchain::parity::ethpoller::custom::api'; + return $self; +} + +1; + +__END__ + +=head1 PLUGIN DESCRIPTION + +Check Parity blockchain accounts, contracts and forks from eth-poller with HTTP GET + +=cut diff --git a/centreon-plugins/blockchain/parity/restapi/mode/eth.pm b/centreon-plugins/blockchain/parity/restapi/mode/eth.pm index de8eb872f..ca604f753 100644 --- a/centreon-plugins/blockchain/parity/restapi/mode/eth.pm +++ b/centreon-plugins/blockchain/parity/restapi/mode/eth.pm @@ -40,21 +40,10 @@ sub set_counters { $self->{maps_counters_type} = [ { name => 'global', cb_prefix_output => 'prefix_module_output', type => 0 }, - { name => 'peer', cb_prefix_output => 'prefix_module_output', type => 0 }, { name => 'block', cb_prefix_output => 'prefix_module_output', type => 0 }, - { name => 'sync', cb_prefix_output => 'prefix_module_output', type => 0 } ]; $self->{maps_counters}->{global} = [ - { label => 'coinbase', nlabel => 'parity.eth.client.coinbase', set => { - key_values => [ { name => 'coinbase' } ], - output_template => "Client coinbase is: %s ", - # closure_custom_perfdata => sub { return 0; } - perfdatas => [ - { label => 'client_coinbase', value => 'coinbase_absolute', template => '%s', min => 0 } - ], - } - }, { label => 'gas_price', nlabel => 'parity.eth.gas.price', set => { key_values => [ { name => 'gas_price' } ], output_template => "The gas price is: %d wei ", @@ -65,90 +54,47 @@ sub set_counters { } ]; - $self->{maps_counters}->{peer} = [ - # { label => 'status', nlabel => 'parity.eth.peers.mining.status', set => { - # key_values => [ { name => 'is_mining' } ], - # output_template => "Client is mining: " . $self->can('custom_mining_status_output'), - # perfdatas => [ - # { label => 'is_mining', value => 'is_mining_absolute', template => '%s', min => 0 } - # ], - # } - # }, - { label => 'status', threshold => 0, set => { - key_values => [ { name => 'is_mining' } ], - closure_custom_calc => \&catalog_status_calc, - closure_custom_output => $self->can('custom_status_output'), - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold - } - }, - { label => 'hashrate', nlabel => 'parity.eth.node.hashrate', set => { - key_values => [ { name => 'hashrate' } ], - output_template => "Node hashrate is: %d/s ", - perfdatas => [ - { label => 'node_hashrate', value => 'hashrate_absolute', template => '%d', min => 0 } - ], - } - }, - ]; - $self->{maps_counters}->{block} = [ - { label => 'block_number', nlabel => 'parity.eth.block.number', set => { - key_values => [ { name => 'block_number' } ], - output_template => "Most recent block number is: %d ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'block_number', value => 'block_number_absolute', template => '%d', min => 0 } - # ], - } - }, - { label => 'block_time', nlabel => 'parity.eth.block.time', set => { - key_values => [ { name => 'block_time' } ], - output_template => "Block time is: %s ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'block_time', value => 'block_time_absolute', template => '%s', min => 0 } - # ], - } - }, - ]; - - $self->{maps_counters}->{sync} = [ - { label => 'sync_start', nlabel => 'parity.eth.sync.start.block', set => { - key_values => [ { name => 'sync_start' } ], - output_template => "Sync start block number is: %d ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'sync_start', value => 'sync_start_absolute', template => '%d', min => 0 } - # ], - } - }, - { label => 'sync_current', nlabel => 'parity.eth.sync.current.block', set => { - key_values => [ { name => 'sync_current' } ], - output_template => "Sync current block number is: %d ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'sync_current', value => 'sync_current_absolute', template => '%d', min => 0 } - # ], - } - }, - { label => 'sync_highest', nlabel => 'parity.eth.sync.highest.block', set => { - key_values => [ { name => 'sync_highest' } ], - output_template => "Sync highest block number is: %d ", - # closure_custom_perfdata => sub { return 0; } + { label => 'block_size', nlabel => 'parity.eth.block.size', set => { + key_values => [ { name => 'block_size' } ], + output_template => "Most recent block size: %d ", perfdatas => [ - { label => 'sync_highest', value => 'sync_highest_absolute', template => '%d', min => 0 } + { label => 'block_size', value => 'block_size_absolute', template => '%d', min => 0 } ], } }, - { label => 'sync', nlabel => 'parity.eth.sync.ratio', set => { - key_values => [ { name => 'sync' } ], - output_template => "Sync ratio is: %d%% ", + { label => 'block_transactions', nlabel => 'parity.eth.block.transactions.number', set => { + key_values => [ { name => 'block_transactions' } ], + output_template => "Block transactions number: %d ", perfdatas => [ - { label => 'sync', value => 'sync_absolute', template => '%d', min => 0 } + { label => 'block_transactions', value => 'block_transactions_absolute', template => '%d', min => 0 } ], } - } + }, + { label => 'block_gas', nlabel => 'parity.eth.block.gas', set => { + key_values => [ { name => 'block_gas' } ], + output_template => "Block gas: %d ", + perfdatas => [ + { label => 'block_gas', value => 'block_gas_absolute', template => '%d', min => 0 } + ], + } + }, + { label => 'block_difficulty', nlabel => 'parity.eth.block.difficulty', set => { + key_values => [ { name => 'block_difficulty' } ], + output_template => "Block difficulty: %f ", + perfdatas => [ + { label => 'block_difficulty', value => 'block_difficulty_absolute', template => '%f', min => 0 } + ], + } + }, + { label => 'block_uncles', nlabel => 'parity.eth.block.difficulty', set => { + key_values => [ { name => 'block_uncles' } ], + output_template => "Block uncles: %d ", + perfdatas => [ + { label => 'block_uncles', value => 'block_uncles_absolute', template => '%d', min => 0 } + ], + } + }, ]; } @@ -190,32 +136,55 @@ sub manage_selection { { method => 'eth_syncing', params => [], id => "7", jsonrpc => "2.0" } ]; my $result = $options{custom}->request_api(method => 'POST', query_form_post => $query_form_post); + + my $gas_price = hex(@{$result}[2]->{result}); # use Data::Dumper; - # print Dumper($result); + # my $length = scalar(@{$$result[5]->{result}->{transactions}}); + # print Dumper($result) ; # conditional formating: my $res_sync = @{$result}[6]->{result} ? hex((@{$result}[6]->{result}->{currentBlock} / @{$result}[6]->{result}->{highestBlock})) * 100 : 100; - my $res_startingBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{startingBlock}) : undef; - my $res_currentBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{currentBlock}) : undef; - my $res_highestBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{highestBlock}) : undef; + my $res_startingBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{startingBlock}) : 'none'; + my $res_currentBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{currentBlock}) : 'none'; + my $res_highestBlock = $res_sync != 100 ? hex(@{$result}[6]->{result}->{highestBlock}) : 'none'; - # Unix time conversion - my $res_timestamp = localtime(hex(@{$result}[5]->{result}->{timestamp})); + # Alerts management + my $cache = Cache::File->new( cache_root => './parity-restapi-cache' ); - $self->{global} = { coinbase => @{$result}[1]->{result}, - gas_price => hex(@{$result}[2]->{result}) }; + if (my $cached_sync = $cache->get('node_sync')) { + if ($cached_sync == 100 && $res_sync < 100) { + #alert + } + } else { + $cache->set('node_sync', $res_sync); + } - $self->{block} = { block_number => defined @{$result}[4]->{result} ? hex(@{$result}[4]->{result}) : 0, - block_time => $res_timestamp }; + if (my $cached_price = $cache->get('gas_price')) { + if ($cached_price != $gas_price) { + #alert + } + } else { + $cache->set('gas_price', $gas_price); + } - $self->{sync} = { sync_start => $res_startingBlock, - sync_current => $res_currentBlock, - sync_highest => $res_highestBlock, - sync => $res_sync }; + $self->{global} = { gas_price => $gas_price }; - $self->{peer} = { is_mining => @{$result}[0]->{result}, - hashrate => hex(@{$result}[3]->{result}) }; + $self->{block} = { block_size => hex(@{$result}[5]->{result}->{size}), + block_gas => hex(@{$result}[5]->{result}->{gasUsed}), + block_difficulty => hex(@{$result}[5]->{result}->{totalDifficulty}), + block_uncles => scalar(@{$$result[5]->{result}->{uncles}}), + block_transactions => scalar(@{$$result[5]->{result}->{transactions}})}; + + $self->{output}->output_add(severity => 'OK', long_msg => '[Node status] is_mining: ' . @{$result}[0]->{result} . ' | sync_start: ' . $res_startingBlock . + ' | sync_current: ' . $res_currentBlock . ' | sync_highest: ' . $res_highestBlock . ' | sync: ' . $res_sync . '%%'); + $self->{output}->output_add(severity => 'OK', long_msg => '[Client] coinbase: ' . @{$result}[1]->{result}); + $self->{output}->output_add(severity => 'OK', long_msg => '[Global] hashrate: ' . hex(@{$result}[3]->{result}) . + ' | block_number: ' . (defined @{$result}[4]->{result} ? hex(@{$result}[4]->{result}) : 0)); + $self->{output}->output_add(severity => 'OK', long_msg => '[Last block] block_time: ' . localtime(hex(@{$result}[5]->{result}->{timestamp})) . ' | block_gas_limit: ' . hex(@{$result}[5]->{result}->{gasLimit}) . + ' | block_miner: ' . @{$result}[5]->{result}->{miner} . ' | block_hash: ' . @{$result}[5]->{result}->{hash} . + ' | last_block_number: ' . hex(@{$result}[5]->{result}->{number})); + } 1; diff --git a/centreon-plugins/blockchain/parity/restapi/mode/net.pm b/centreon-plugins/blockchain/parity/restapi/mode/net.pm index d5e8f88cf..02ba31028 100644 --- a/centreon-plugins/blockchain/parity/restapi/mode/net.pm +++ b/centreon-plugins/blockchain/parity/restapi/mode/net.pm @@ -43,15 +43,7 @@ sub set_counters { ]; $self->{maps_counters}->{network} = [ - { label => 'status', threshold => 0, set => { - key_values => [ { name => 'listening' } ], - closure_custom_calc => \&catalog_status_calc, - closure_custom_output => $self->can('custom_status_output'), - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => \&catalog_status_threshold - } - }, - { label => 'peers', nlabel => 'parity.network.peers.count', set => { + { label => 'peers', nlabel => 'parity.network.peers.count', set => { key_values => [ { name => 'peers' } ], output_template => "connected peers: %s ", perfdatas => [ @@ -96,9 +88,24 @@ sub manage_selection { my $result = $options{custom}->request_api(method => 'POST', query_form_post => $query_form_post); - $self->{network} = { listening => @{$result}[0]->{result}, - peers => hex(@{$result}[1]->{result}) }; + my $peer_count = hex(@{$result}[1]->{result}); + # Alerts management + my $cache = Cache::File->new( cache_root => './parity-restapi-cache' ); + + if (my $cached_count = $cache->get('peers_count')) { + if ($peer_count < $cached_count) { + #alert + } elsif ($peer_count > $cached_count) { + #alert + } + } else { + $cache->set('peers_count', $peer_count); + } + + $self->{network} = { peers => hex(@{$result}[1]->{result}) }; + + $self->{output}->output_add(long_msg => "[Node] is_listening: " . $peer_count, severity => 'OK'); } 1; diff --git a/centreon-plugins/blockchain/parity/restapi/mode/parity.pm b/centreon-plugins/blockchain/parity/restapi/mode/parity.pm index 0050e1ce9..8823d1673 100644 --- a/centreon-plugins/blockchain/parity/restapi/mode/parity.pm +++ b/centreon-plugins/blockchain/parity/restapi/mode/parity.pm @@ -24,108 +24,18 @@ use base qw(centreon::plugins::templates::counter); use strict; use warnings; +use Cache::File; use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold catalog_status_calc); sub set_counters { my ($self, %options) = @_; $self->{maps_counters_type} = [ -# { name => 'global', cb_prefix_output => 'prefix_module_output', type => 0 }, - { name => 'node', cb_prefix_output => 'prefix_module_output', type => 0 }, - { name => 'mempool', cb_prefix_output => 'prefix_module_output', type => 0 }, - { name => 'network', cb_prefix_output => 'prefix_module_output', type => 0 } - ]; - - $self->{maps_counters}->{global} = [ - { label => 'parity_version', nlabel => 'parity.version', threshold => 0, set => { - key_values => [ { name => 'parity_version' } ], - output_template => "Parity version is: %s ", - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => sub { return 0; } - # perfdatas => [ - # { label => 'parity_version', value => 'parity_version_absolute', template => '%s', min => 0 } - # ], - } - }, - { label => 'parity_version_hash', nlabel => 'parity.version.hash', set => { - key_values => [ { name => 'parity_version_hash' } ], - output_template => "Parity version hash is: %s ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'parity_version_hash', value => 'parity_version_hash_absolute', template => '%s', min => 0 } - # ], - } - }, - { label => 'chain_name', nlabel => 'parity.chain.name', set => { - key_values => [ { name => 'chain_name' } ], - output_template => "Chain name is: %s ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'chain_name', value => 'chain_name_absolute', template => '%s', min => 0 } - # ], - } - }, - ]; - - $self->{maps_counters}->{node} = [ - { label => 'enode', nlabel => 'parity.node.enode.uri', threshold => 0, set => { - key_values => [ { name => 'enode' } ], - output_template => "Node enode URI: %s ", - closure_custom_perfdata => sub { return 0; }, - closure_custom_threshold_check => sub { return 0; } - # perfdatas => [ - # { label => 'enode', value => 'enode_absolute', template => '%s', min => 0 } - # ], - } - }, - { label => 'node_name', nlabel => 'parity.node.name', set => { - key_values => [ { name => 'node_name' } ], - output_template => "Node name: %s ", - closure_custom_perfdata => sub { return 0; } - # perfdatas => [ - # { label => 'node_name', value => 'node_name_absolute', template => '%s', min => 0 } - # ], - } - } - ]; - - $self->{maps_counters}->{network} = [ - { label => 'peers_connected', nlabel => 'parity.peers.connected', set => { - key_values => [ { name => 'peers_connected' } ], - output_template => "Number of connected peers: %d ", - perfdatas => [ - { label => 'peers_connected', value => 'peers_connected_absolute', template => '%d', min => 0 } - ], - } - }, - { label => 'peers_max', nlabel => 'parity.peers.max.connected', set => { - key_values => [ { name => 'peers_max' } ], - output_template => "Maximum number of connected peers: %d ", - perfdatas => [ - { label => 'peers_max', value => 'peers_max_absolute', template => '%d', min => 0 } - ], - } - }, - { label => 'peers', nlabel => 'parity.peers', set => { - key_values => [ { name => 'peers' } ], - output_template => "Peers: %d ", - perfdatas => [ - { label => 'peers', value => 'peers_absolute', template => '%d', min => 0 } - ], - } - } - ]; + { name => 'mempool', cb_prefix_output => 'prefix_module_output', type => 0 } + ]; $self->{maps_counters}->{mempool} = [ - { label => 'pending_transactions', nlabel => 'parity.pending.transactions', set => { - key_values => [ { name => 'pending_transactions' } ], - output_template => "Pending transactions: %d ", - perfdatas => [ - { label => 'pending_transactions', value => 'pending_transactions_absolute', template => '%d', min => 0 } - ], - } - }, - { label => 'mempool', nlabel => 'parity.mempol.capacity', set => { + { label => 'mempool', nlabel => 'parity.mempol.usage', set => { key_values => [ { name => 'mempool' } ], output_template => "Mempool: %d %% ", perfdatas => [ @@ -172,35 +82,46 @@ sub manage_selection { { method => 'parity_netPeers', params => [], id => "4", jsonrpc => "2.0" }, { method => 'parity_enode', params => [], id => "5", jsonrpc => "2.0" }, { method => 'parity_nodeName', params => [], id => "6", jsonrpc => "2.0" }, - { method => 'parity_transactionsLimit', params => [], id => "7", jsonrpc => "2.0" } ]; # parity_transactionsLimit could be done once, at the beginning of the process + { method => 'parity_transactionsLimit', params => [], id => "7", jsonrpc => "2.0" } ]; #TO CHECK parity_transactionsLimit could be done once, at the beginning of the process - my $result = $options{custom}->request_api(method => 'POST', query_form_post => $query_form_post); + use Data::Dumper; + # print Dumper($result); + # Parity version construction my $res_parity_version = @{$result}[0]->{result}->{version}->{major} . '.' . @{$result}[0]->{result}->{version}->{minor} . '.' . @{$result}[0]->{result}->{version}->{patch}; + # Alerts management + my $cache = Cache::File->new( cache_root => './parity-restapi-cache' ); + + if (my $cached_version = $cache->get('parity_version')) { + if ($res_parity_version ne $cached_version) { + #alert + } + } else { + $cache->set('parity_version', $res_parity_version); + } + + if (my $cached_name = $cache->get('chain_name')) { + if ($cached_name ne @{$result}[1]->{result}) { + #alert + } + } else { + $cache->set('chain_name', @{$result}[1]->{result}); + } # use Data::Dumper; - # print Dumper($res_parity_version); + # print Dumper($result); - $self->{output}->output_add(long_msg => "[config] chain name: " . @{$result}[1]->{result} . " parity version: " . $res_parity_version . " version_hash: " . @{$result}[0]->{result}->{hash}, - severity => 'OK'); - -# $self->{global} = { parity_version => $res_parity_version, -# parity_version_hash => @{$result}[0]->{result}->{hash}, -# chain_name => @{$result}[1]->{result} }; - -# $self->{node} = { enode => @{$result}[4]->{result}, -# node_name => @{$result}[5]->{result} }; - - $self->{network} = { peers_connected => @{$result}[3]->{result}->{connected}, - peers_max => @{$result}[3]->{result}->{max}, - peers => length(@{$result}[3]->{result}->{peers}) }; - - $self->{mempool} = { pending_transactions => length(@{$result}[2]->{result}), - mempool => @{$result}[2]->{result} / @{$result}[6]->{result} * 100 }; + $self->{output}->output_add(long_msg => "[config] chain name: " . @{$result}[1]->{result} . " | parity version: " . $res_parity_version . " | version_hash: " + . @{$result}[0]->{result}->{hash}, severity => 'OK'); + $self->{output}->output_add(long_msg => "[Network] peers_connected: " . @{$result}[3]->{result}->{connected} . " | peers_max: " . @{$result}[3]->{result}->{max} . " | peers: " + . scalar(@{$$result[3]->{result}->{peers}}), severity => 'OK'); + $self->{output}->output_add(long_msg => "[Node] node_name: " . @{$result}[5]->{result} . " | enode: " . @{$result}[4]->{result} , severity => 'OK'); + $self->{output}->output_add(long_msg => "[Node] pending_transactions: " . scalar(@{$$result[2]->{result}}), severity => 'OK'); + $self->{mempool} = { mempool => scalar(@{$$result[2]->{result}}) / @{$result}[6]->{result} * 100 }; #TO CHECK division entière } 1;