File centreon_mysql.pl of Package monitoring-plugins-centreon-mysql
#!/usr/bin/perl
# This chunk of stuff was generated by App::FatPacker. To find the original
# file's code, look for the end of this BEGIN block or the string 'FATPACK'
BEGIN {
my %fatpacked;
$fatpacked{"centreon/common/protocols/sql/mode/collection.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_COMMON_PROTOCOLS_SQL_MODE_COLLECTION';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::protocols::sql::mode::collection;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use JSON::XS;
use Safe;
use centreon::plugins::misc;
use centreon::plugins::statefile;
use Digest::MD5 qw(md5_hex);
use Time::HiRes qw(gettimeofday tv_interval);
sub custom_select_threshold {
my ($self, %options) = @_;
my $status = 'ok';
our $expand = $self->{result_values}->{expand};
if (defined($self->{result_values}->{config}->{critical}) && $self->{result_values}->{config}->{critical} &&
$self->{instance_mode}->{safe}->reval($self->{result_values}->{config}->{critical})) {
$status = 'critical';
} elsif (defined($self->{result_values}->{config}->{warning}) && $self->{result_values}->{config}->{warning} ne '' &&
$self->{instance_mode}->{safe}->reval($self->{result_values}->{config}->{warning})) {
$status = 'warning';
} elsif (defined($self->{result_values}->{config}->{unknown}) && $self->{result_values}->{config}->{unknown} &&
$self->{instance_mode}->{safe}->reval($self->{result_values}->{config}->{unknown})) {
$status = 'unknown';
}
if ($@) {
$self->{output}->add_option_msg(short_msg => 'Unsafe code evaluation: ' . $@);
$self->{output}->option_exit();
}
$self->{result_values}->{last_status} = $status;
return $status;
}
sub custom_select_perfdata {
my ($self, %options) = @_;
return if (!defined($self->{result_values}->{config}->{perfdatas}));
foreach (@{$self->{result_values}->{config}->{perfdatas}}) {
next if (!defined($_->{value}) || $_->{value} !~ /^[+-]?\d+(?:\.\d+)?$/);
$self->{output}->perfdata_add(%$_);
}
}
sub custom_select_output {
my ($self, %options) = @_;
return '' if (
$self->{result_values}->{last_status} eq 'ok' && defined($self->{result_values}->{config}->{formatting}) &&
defined($self->{result_values}->{config}->{formatting}->{display_ok}) &&
$self->{result_values}->{config}->{formatting}->{display_ok} =~ /^false|0$/
);
my $format;
if (defined($self->{result_values}->{config}->{ 'formatting_' . $self->{result_values}->{last_status} })) {
$format = $self->{result_values}->{config}->{ 'formatting_' . $self->{result_values}->{last_status} };
} elsif (defined($self->{result_values}->{config}->{formatting})) {
$format = $self->{result_values}->{config}->{formatting};
}
if (defined($format)) {
return sprintf(
$format->{printf_msg}, @{$format->{printf_var}}
);
}
# without formatting: [name: xxxxxx][test: xxxx][test2: xxx][mytable.plcRead: xxx][mytable.plcWrite: xxx]
my $output = '';
foreach (sort keys %{$self->{result_values}->{expand}}) {
next if (/^(?:constants|builtin)\./);
$output .= '[' . $_ . ': ' . (defined($self->{result_values}->{expand}->{$_}) ? $self->{result_values}->{expand}->{$_} : '') . ']';
}
return $output;
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'selections', type => 1, message_multiple => 'All selections are ok', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{selections} = [
{ label => 'select', threshold => 0, set => {
key_values => [ { name => 'expand' }, { name => 'config' } ],
closure_custom_output => $self->can('custom_select_output'),
closure_custom_perfdata => $self->can('custom_select_perfdata'),
closure_custom_threshold_check => $self->can('custom_select_threshold')
}
}
];
}
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 => {
'config:s' => { name => 'config' },
'filter-selection:s%' => { name => 'filter_selection' },
'constant:s%' => { name => 'constant' }
});
$self->{safe} = Safe->new();
$self->{safe}->share('$expand');
$self->{safe_func} = Safe->new();
$self->{safe_func}->share('$assign_var');
$self->{builtin} = {};
$self->{sql_cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (!defined($self->{option_results}->{config})) {
$self->{output}->add_option_msg(short_msg => 'Please set config option');
$self->{output}->option_exit();
}
$self->{sql_cache}->check_options(option_results => $self->{option_results});
}
sub read_config {
my ($self, %options) = @_;
my $content;
if ($self->{option_results}->{config} =~ /\n/m || ! -f "$self->{option_results}->{config}") {
$content = $self->{option_results}->{config};
} else {
$content = do {
local $/ = undef;
if (!open my $fh, '<', $self->{option_results}->{config}) {
$self->{output}->add_option_msg(short_msg => "Could not open file $self->{option_results}->{config} : $!");
$self->{output}->option_exit();
}
<$fh>;
};
}
eval {
$self->{config} = JSON::XS->new->decode($content);
};
if ($@) {
$self->{output}->output_add(long_msg => "json config error: $@", debug => 1);
$self->{output}->add_option_msg(short_msg => 'Cannot decode json config');
$self->{output}->option_exit();
}
}
sub get_map_value {
my ($self, %options) = @_;
return undef if (
!defined($self->{config}->{mapping}) ||
!defined($self->{config}->{mapping}->{ $options{map} })
);
return '' if (!defined($self->{config}->{mapping}->{ $options{map} }->{ $options{value} }));
return $self->{config}->{mapping}->{ $options{map} }->{ $options{value} };
}
sub validate_name {
my ($self, %options) = @_;
if (!defined($options{name})) {
$self->{output}->add_option_msg(short_msg => "name attribute is missing $options{section}");
$self->{output}->option_exit();
}
if ($options{name} !~ /^[a-zA-Z0-9_]+$/) {
$self->{output}->add_option_msg(short_msg => 'incorrect name attribute: ' . $options{name});
$self->{output}->option_exit();
}
}
sub collect_sql_tables {
my ($self, %options) = @_;
return if (!defined($self->{config}->{sql}->{tables}));
foreach my $table (@{$self->{config}->{sql}->{tables}}) {
$self->validate_name(name => $table->{name}, section => "[sql > tables]");
if (!defined($table->{query}) || $table->{query} eq '') {
$self->{output}->add_option_msg(short_msg => "query attribute is missing [sql > tables > $table->{name}]");
$self->{output}->option_exit();
}
# substitute constants
$self->{expand} = $self->set_constants();
$table->{query} = $self->substitute_string(value => $table->{query});
my $timing0 = [gettimeofday];
$options{sql}->query(query => $table->{query});
$self->add_builtin(name => 'sqlExecutionTime.' . $table->{name}, value => tv_interval($timing0, [gettimeofday]));
$self->{sql_collected}->{tables}->{ $table->{name} } = {};
my $i = 0;
while (my $entry = $options{sql}->fetchrow_hashref()) {
my $instance = $i;
if (defined($table->{instances})) {
$instance = '';
my $append = '';
foreach (@{$table->{instances}}) {
if (!defined($entry->{$_})) {
$self->{output}->add_option_msg(short_msg => "cannot get instance '$_' in result [sql > tables > $table->{name}]");
$self->{output}->option_exit();
}
$instance .= $append . $entry->{$_};
$append = ':';
}
}
if (defined($table->{entries})) {
foreach (@{$table->{entries}}) {
if (!defined($_->{id}) || !defined($entry->{ $_->{id} })) {
$self->{output}->add_option_msg(short_msg => "id attribute is missing or wrong [sql > tables > $table->{name} > entries]");
$self->{output}->option_exit();
}
if (defined($_->{map}) && $_->{map} ne '') {
if (!defined($self->{config}->{mapping}) || !defined($self->{config}->{mapping}->{ $_->{map} })) {
$self->{output}->add_option_msg(short_msg => "unknown map attribute [sql > tables > $table->{name} > $_->{id}]: $_->{map}");
$self->{output}->option_exit();
}
$entry->{ $_->{id} } = $self->{config}->{mapping}->{ $_->{map} }->{ $entry->{ $_->{id} } };
}
if (defined($_->{sampling}) && $_->{sampling} == 1) {
$self->{sql_collected_sampling}->{tables}->{ $table->{name} } = {}
if (!defined($self->{sql_collected_sampling}->{tables}->{ $table->{name} }));
$self->{sql_collected_sampling}->{tables}->{ $table->{name} }->{$instance}->{ $_->{id} } = $entry->{ $_->{id} };
}
}
}
$self->{sql_collected}->{tables}->{ $table->{name} }->{$instance} = $entry;
$i++;
}
}
}
sub is_sql_cache_enabled {
my ($self, %options) = @_;
return 0 if (
!defined($self->{config}->{sql}->{cache}) ||
!defined($self->{config}->{sql}->{cache}->{enable}) ||
$self->{config}->{sql}->{cache}->{enable} !~ /^true|1$/i
);
return 1;
}
sub use_sql_cache {
my ($self, %options) = @_;
return 0 if ($self->is_sql_cache_enabled() == 0);
my $has_cache_file = $self->{sql_cache}->read(
statefile => 'cache_sql_collection_' . $options{sql}->get_unique_id4save() . '_' .
md5_hex($self->{option_results}->{config})
);
$self->{sql_collected} = $self->{sql_cache}->get(name => 'sql_collected');
my $reload = defined($self->{config}->{sql}->{cache}->{reload}) && $self->{config}->{sql}->{cache}->{reload} =~ /(\d+)/ ?
$self->{config}->{sql}->{cache}->{reload} : 30;
return 0 if (
$has_cache_file == 0 ||
!defined($self->{sql_collected}) ||
((time() - $self->{sql_collected}->{epoch}) > ($reload * 60))
);
return 1;
}
sub save_sql_cache {
my ($self, %options) = @_;
return 0 if ($self->is_sql_cache_enabled() == 0);
$self->{sql_cache}->write(data => { sql_collected => $self->{sql_collected} });
}
sub collect_sql_sampling {
my ($self, %options) = @_;
return if ($self->{sql_collected}->{sampling} == 0);
my $has_cache_file = $self->{sql_cache}->read(
statefile => 'cache_sql_collection_sampling_' . $options{sql}->get_unique_id4save() . '_' .
md5_hex($self->{option_results}->{config})
);
my $sql_collected_sampling_old = $self->{sql_cache}->get(name => 'sql_collected_sampling');
# with cache, we need to load the sampling cache maybe. please a statefile-suffix to get uniq files.
# sampling with a global cache can be a nonsense
if (!defined($self->{sql_collected_sampling})) {
$self->{sql_collected_sampling} = $sql_collected_sampling_old;
}
my $delta_time;
if (defined($sql_collected_sampling_old->{epoch})) {
$delta_time = $self->{sql_collected_sampling}->{epoch} - $sql_collected_sampling_old->{epoch};
$delta_time = 1 if ($delta_time <= 0);
}
foreach my $tbl_name (keys %{$self->{sql_collected_sampling}->{tables}}) {
foreach my $instance (keys %{$self->{sql_collected_sampling}->{tables}->{$tbl_name}}) {
foreach my $attr (keys %{$self->{sql_collected_sampling}->{tables}->{$tbl_name}->{$instance}}) {
next if (
!defined($sql_collected_sampling_old->{tables}->{$tbl_name}) ||
!defined($sql_collected_sampling_old->{tables}->{$tbl_name}->{$instance}) ||
!defined($sql_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr}) ||
$sql_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr} !~ /\d/
);
my $old = $sql_collected_sampling_old->{tables}->{$tbl_name}->{$instance}->{$attr};
my $diff = $self->{sql_collected_sampling}->{tables}->{$tbl_name}->{$instance}->{$attr} - $old;
my $diff_counter = $diff;
$diff_counter = $self->{sql_collected_sampling}->{tables}->{$tbl_name}->{$instance}->{$attr} if ($diff_counter < 0);
$self->{sql_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'Diff' } = $diff;
$self->{sql_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'DiffCounter' } = $diff_counter;
if (defined($delta_time)) {
$self->{sql_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'PerSeconds' } = $diff_counter / $delta_time;
$self->{sql_collected}->{tables}->{$tbl_name}->{$instance}->{ $attr . 'PerMinutes' } = $diff_counter / $delta_time / 60;
}
}
}
}
$self->{sql_cache}->write(data => { sql_collected_sampling => $self->{sql_collected_sampling} });
}
sub connect_sql {
my ($self, %options) = @_;
my ($status, $dontquit) = ('succeeded', 0);
if (defined($self->{config}->{sql}->{connectHandleError}) && $self->{config}->{sql}->{connectHandleError} == 1) {
$dontquit = 1;
}
my ($rv, $message) = $options{sql}->connect(dontquit => $dontquit);
if ($rv == -1) {
$status = 'failed';
}
$self->add_builtin(name => 'connectStatus', value => $status);
$self->add_builtin(name => 'connectMessage', value => defined($message) ? $message : '-');
return $rv;
}
sub collect_sql {
my ($self, %options) = @_;
if (!defined($self->{config}->{sql})) {
$self->{output}->add_option_msg(short_msg => 'please set sql config');
$self->{output}->option_exit();
}
$self->add_builtin(name => 'currentTime', value => time());
if ($self->use_sql_cache(sql => $options{sql}) == 0) {
$self->{sql_collected_sampling} = { tables => {}, epoch => time() };
$self->{sql_collected} = { tables => {}, epoch => time(), sampling => 0 };
if ($self->connect_sql(%options) == 0) {
$self->collect_sql_tables(sql => $options{sql});
}
$self->{sql_collected}->{sampling} = 1 if (
scalar(keys(%{$self->{sql_collected_sampling}->{tables}})) > 0
);
$self->save_sql_cache();
}
$self->collect_sql_sampling(sql => $options{sql});
}
sub exist_table_name {
my ($self, %options) = @_;
return 1 if (defined($self->{sql_collected}->{tables}->{ $options{name} }));
return 0;
}
sub get_local_variable {
my ($self, %options) = @_;
return $self->{expand}->{ $options{name} };
}
sub set_local_variable {
my ($self, %options) = @_;
$self->{expand}->{ $options{name} } = $options{value};
}
sub get_table {
my ($self, %options) = @_;
return undef if (
!defined($self->{sql_collected}->{tables}->{ $options{table} })
);
return $self->{sql_collected}->{tables}->{ $options{table} };
}
sub get_table_instance {
my ($self, %options) = @_;
return undef if (
!defined($self->{sql_collected}->{tables}->{ $options{table} }) ||
!defined($self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} })
);
return $self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} };
}
sub get_table_attribute_value {
my ($self, %options) = @_;
return undef if (
!defined($self->{sql_collected}->{tables}->{ $options{table} }) ||
!defined($self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} }) ||
!defined($self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} }->{ $options{attribute} })
);
return $self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} }->{ $options{attribute} };
}
sub set_table_attribute_value {
my ($self, %options) = @_;
$self->{sql_collected}->{tables}->{ $options{table} } = {}
if (!defined($self->{sql_collected}->{tables}->{ $options{table} }));
$self->{sql_collected}->{tables}->{ $options{table} } = {}
if (!defined($self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} }));
$self->{sql_collected}->{tables}->{ $options{table} }->{ $options{instance} }->{ $options{attribute} } = $options{value};
}
sub get_special_variable_value {
my ($self, %options) = @_;
my $data;
if ($options{type} == 0) {
$data = $self->get_local_variable(name => $options{label});
} elsif ($options{type} == 2) {
$data = $self->get_table(table => $options{table});
} elsif ($options{type} == 4) {
$data = $self->get_table_attribute_value(
table => $options{table},
instance => $options{instance},
attribute => $options{label}
);
}
return $data;
}
sub set_special_variable_value {
my ($self, %options) = @_;
my $data;
if ($options{type} == 0) {
$data = $self->set_local_variable(name => $options{label}, value => $options{value});
} elsif ($options{type} == 4) {
$data = $self->set_table_attribute_value(
table => $options{table},
instance => $options{instance},
attribute => $options{label},
value => $options{value}
);
}
return $data;
}
sub strcmp {
my ($self, %options) = @_;
my @cmp = split //, $options{test};
for (my $i = 0; $i < scalar(@cmp); $i++) {
return 0 if (
!defined($options{chars}->[ $options{start} + $i ]) ||
$options{chars}->[ $options{start} + $i ] ne $cmp[$i]
);
}
return 1;
}
sub parse_forward {
my ($self, %options) = @_;
my ($string, $i) = ('', 0);
while (1) {
return (1, 'cannot find ' . $options{stop} . ' character')
if (!defined($options{chars}->[ $options{start} + $i ]));
last if ($options{chars}->[ $options{start} + $i ] =~ /$options{stop}/);
return (1, "character '" . $options{chars}->[ $options{start} + $i ] . "' forbidden")
if ($options{chars}->[ $options{start} + $i ] !~ /$options{allowed}/);
$string .= $options{chars}->[ $options{start} + $i ];
$i++;
}
return (0, undef, $options{start} + $i, $string);
}
=pod
managed variables:
%(sql.tables.servers)
%(sql.tables.servers.[1])
%(sql.tables.servers.[1].plop)
%(sql.tables.servers.[%(mytable.instance)]
%(sql.tables.servers.[%(sql.tables.servers.[%(mytable.instance)].name)]
%(test2)
%(mytable.test)
result:
- type:
0=%(test) (label)
2=%(sql.tables.test)
3=%(sql.tables.test.[2])
4=%(sql.tables.test.[2].attrname)
=cut
sub parse_sql_tables {
my ($self, %options) = @_;
my ($code, $msg_error, $end, $table_label, $instance_label, $label);
($code, $msg_error, $end, $table_label) = $self->parse_forward(
chars => $options{chars},
start => $options{start},
allowed => '[a-zA-Z0-9_]',
stop => '[).]'
);
if ($code) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " $msg_error");
$self->{output}->option_exit();
}
if (!$self->exist_table_name(name => $table_label)) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " unknown table '$table_label'");
$self->{output}->option_exit();
}
if ($options{chars}->[$end] eq ')') {
return { type => 2, end => $end, table => $table_label };
}
# instance part managenent
if (!defined($options{chars}->[$end + 1]) || $options{chars}->[$end + 1] ne '[') {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable sql.tables character '[' mandatory");
$self->{output}->option_exit();
}
if ($self->strcmp(chars => $options{chars}, start => $end + 2, test => '%(')) {
my $result = $self->parse_special_variable(chars => $options{chars}, start => $end + 2);
# type allowed: 0,4
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . ' special variable type not allowed');
$self->{output}->option_exit();
}
$end = $result->{end} + 1;
if ($result->{type} == 0) {
$instance_label = $self->get_local_variable(name => $result->{label});
} elsif ($result->{type} == 4) {
$instance_label = $self->get_table_attribute_value(
table => $result->{table},
instance => $result->{instance},
attribute => $result->{label}
);
}
$instance_label = defined($instance_label) ? $instance_label : '';
} else {
($code, $msg_error, $end, $instance_label) = $self->parse_forward(
chars => $options{chars},
start => $end + 2,
allowed => '[^\]]',
stop => '[\]]'
);
if ($code) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " $msg_error");
$self->{output}->option_exit();
}
}
if (!defined($options{chars}->[$end + 1]) ||
$options{chars}->[$end + 1] !~ /[.)]/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . ' special variable sql.tables character [.)] missing');
$self->{output}->option_exit();
}
if ($options{chars}->[$end + 1] eq ')') {
return { type => 3, end => $end + 1, table => $table_label, instance => $instance_label };
}
($code, $msg_error, $end, $label) = $self->parse_forward(
chars => $options{chars},
start => $end + 2,
allowed => '[a-zA-Z0-9_]',
stop => '[)]'
);
if ($code) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " $msg_error");
$self->{output}->option_exit();
}
return { type => 4, end => $end, table => $table_label, instance => $instance_label, label => $label };
}
sub parse_sql_type {
my ($self, %options) = @_;
if ($self->strcmp(chars => $options{chars}, start => $options{start}, test => 'tables.')) {
return $self->parse_sql_tables(chars => $options{chars}, start => $options{start} + 7);
} else {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . ' special variable sql not followed by tables');
$self->{output}->option_exit();
}
}
sub parse_special_variable {
my ($self, %options) = @_;
my $start = $options{start};
if ($options{chars}->[$start] ne '%' ||
$options{chars}->[$start + 1] ne '(') {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . ' special variable not starting by %(');
$self->{output}->option_exit();
}
my $result = { start => $options{start} };
if ($self->strcmp(chars => $options{chars}, start => $start + 2, test => 'sql.')) {
my $parse = $self->parse_sql_type(chars => $options{chars}, start => $start + 2 + 4);
$result = { %$parse, %$result };
} else {
my ($code, $msg_error, $end, $label) = $self->parse_forward(
chars => $options{chars},
start => $start + 2,
allowed => '[a-zA-Z0-9\._]',
stop => '[)]'
);
if ($code) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " $msg_error");
$self->{output}->option_exit();
}
$result->{end} = $end;
$result->{type} = 0;
$result->{label} = $label;
}
return $result;
}
sub substitute_string {
my ($self, %options) = @_;
my $arr = [split //, $options{value}];
my $results = {};
my $last_end = -1;
while ($options{value} =~ /\Q%(\E/g) {
next if ($-[0] < $last_end);
my $result = $self->parse_special_variable(chars => $arr, start => $-[0]);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed");
$self->{output}->option_exit();
}
$last_end = $result->{end};
$results->{ $result->{start} } = $result;
}
my $end = -1;
my $str = '';
for (my $i = 0; $i < scalar(@$arr); $i++) {
next if ($i <= $end);
if (defined($results->{$i})) {
my $data = $self->get_special_variable_value(%{$results->{$i}});
$end = $results->{$i}->{end};
$str .= defined($data) ? $data : '';
} else {
$str .= $arr->[$i];
}
}
return $str;
}
sub add_builtin {
my ($self, %options) = @_;
$self->{builtin}->{ $options{name} } = $options{value};
}
sub set_builtin {
my ($self, %options) = @_;
foreach (keys %{$self->{builtin}}) {
$self->{expand}->{ 'builtin.' . $_ } = $self->{builtin}->{$_};
}
}
sub set_constants {
my ($self, %options) = @_;
my $constants = {};
if (defined($self->{config}->{constants})) {
foreach (keys %{$self->{config}->{constants}}) {
$constants->{'constants.' . $_} = $self->{config}->{constants}->{$_};
}
}
foreach (keys %{$self->{option_results}->{constant}}) {
$constants->{'constants.' . $_} = $self->{option_results}->{constant}->{$_};
}
return $constants;
}
sub set_expand_table {
my ($self, %options) = @_;
return if (!defined($options{expand}));
foreach my $name (keys %{$options{expand}}) {
$self->{current_section} = '[' . $options{section} . ' > ' . $name . ']';
my $result = $self->parse_special_variable(chars => [split //, $options{expand}->{$name}], start => 0);
if ($result->{type} != 3) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed");
$self->{output}->option_exit();
}
my $table = $self->get_table_instance(table => $result->{table}, instance => $result->{instance});
next if (!defined($table));
$self->{expand}->{ $name . '.instance' } = $result->{instance};
foreach (keys %$table) {
$self->{expand}->{ $name . '.' . $_ } = $table->{$_};
}
}
}
sub set_expand {
my ($self, %options) = @_;
return if (!defined($options{expand}));
foreach my $name (keys %{$options{expand}}) {
$self->{current_section} = '[' . $options{section} . ' > ' . $name . ']';
$self->{expand}->{$name} = $self->substitute_string(value => $options{expand}->{$name});
}
}
sub exec_func_map {
my ($self, %options) = @_;
if (!defined($options{map_name}) || $options{map_name} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set map_name attribute");
$self->{output}->option_exit();
}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|1|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my $value = $self->get_map_value(value => $data, map => $options{map_name});
if (!defined($value)) {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} unknown map attribute: $options{map_name}");
$self->{output}->option_exit();
}
my $save = $result;
if (defined($options{save}) && $options{save} ne '') {
$save = $self->parse_special_variable(chars => [split //, $options{save}], start => 0);
if ($save->{type} !~ /^(?:0|1|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute");
$self->{output}->option_exit();
}
} elsif (defined($options{dst}) && $options{dst} ne '') {
$save = $self->parse_special_variable(chars => [split //, $options{dst}], start => 0);
if ($save->{type} !~ /^(?:0|1|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in dst attribute");
$self->{output}->option_exit();
}
}
$self->set_special_variable_value(value => $value, %$save);
}
sub scale {
my ($self, %options) = @_;
my ($src_quantity, $src_unit) = (undef, 'B');
if ($options{src_unit} =~ /([kmgtpe])?(b)/i) {
$src_quantity = $1;
$src_unit = $2;
}
my ($dst_quantity, $dst_unit) = ('auto', $src_unit);
if ($options{dst_unit} =~ /([kmgtpe])?(b)/i) {
$dst_quantity = $1;
$dst_unit = $2;
}
my $base = 1024;
$options{value} *= 8 if ($dst_unit eq 'b' && $src_unit eq 'B');
$options{value} /= 8 if ($dst_unit eq 'B' && $src_unit eq 'b');
$base = 1000 if ($dst_unit eq 'b');
my %expo = (k => 1, m => 2, g => 3, t => 4, p => 5, e => 6);
my $src_expo = 0;
$src_expo = $expo{ lc($src_quantity) } if (defined($src_quantity));
if (defined($dst_quantity) && $dst_quantity eq 'auto') {
my @auto = ('', 'k', 'm', 'g', 't', 'p', 'e');
for (; $src_expo < scalar(@auto); $src_expo++) {
last if ($options{value} < $base);
$options{value} = $options{value} / $base;
}
return ($options{value}, uc($auto[$src_expo]) . $dst_unit);
}
my $dst_expo = 0;
$dst_expo = $expo{ lc($dst_quantity) } if (defined($dst_quantity));
if ($dst_expo - $src_expo > 0) {
$options{value} = $options{value} / ($base ** ($dst_expo - $src_expo));
} elsif ($dst_expo - $src_expo < 0) {
$options{value} = $options{value} * ($base ** (($dst_expo - $src_expo) * -1));
}
return ($options{value}, $options{dst_unit});
}
sub exec_func_scale {
my ($self, %options) = @_;
#{
# "type": "scale",
# "src": "%(memoryUsed)",
# "src_unit": "KB", (default: 'B')
# "dst_unit": "auto", (default: 'auto')
# "save_value": "%(memoryUsedScaled)",
# "save_unit": "%(memoryUsedUnit)"
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my ($save_value, $save_unit) = $self->scale(
value => $data,
src_unit => $options{src_unit},
dst_unit => $options{dst_unit}
);
if (defined($options{save_value}) && $options{save_value} ne '') {
my $var_save_value = $self->parse_special_variable(chars => [split //, $options{save_value}], start => 0);
if ($var_save_value->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save_value attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $save_value, %$var_save_value);
}
if (defined($options{save_unit}) && $options{save_unit} ne '') {
my $var_save_unit = $self->parse_special_variable(chars => [split //, $options{save_unit}], start => 0);
if ($var_save_unit->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save_value attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $save_unit, %$var_save_unit);
}
}
sub exec_func_second2human {
my ($self, %options) = @_;
#{
# "type": "second2human",
# "src": "%(duration)",
# "save_value": "%(humanDuration)",
# "start": "d",
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my ($str, $str_append) = ('', '');
my $periods = [
{ unit => 'y', value => 31556926 },
{ unit => 'M', value => 2629743 },
{ unit => 'w', value => 604800 },
{ unit => 'd', value => 86400 },
{ unit => 'h', value => 3600 },
{ unit => 'm', value => 60 },
{ unit => 's', value => 1 },
];
my %values = ('y' => 1, 'M' => 2, 'w' => 3, 'd' => 4, 'h' => 5, 'm' => 6, 's' => 7);
my $sign = '';
if ($data < 0) {
$sign = '-';
$data = abs($data);
}
foreach (@$periods) {
next if (defined($options{start}) && $values{$_->{unit}} < $values{$options{start}});
my $count = int($data / $_->{value});
next if ($count == 0);
$str .= $str_append . $count . $_->{unit};
$data = $data % $_->{value};
$str_append = ' ';
}
if ($str eq '') {
$str = $data;
$str .= $options{start} if (defined($options{start}));
}
if (defined($options{save_value}) && $options{save_value} ne '') {
my $var_save_value = $self->parse_special_variable(chars => [split //, $options{save_value}], start => 0);
if ($var_save_value->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save_value attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $sign . $str, %$var_save_value);
}
}
sub exec_func_date2epoch {
my ($self, %options) = @_;
if (!defined($self->{module_datetime_loaded})) {
centreon::plugins::misc::mymodule_load(
module => 'DateTime',
error_msg => "Cannot load module 'DateTime'."
);
$self->{module_datetime_loaded} = 1;
}
#{
# "type": "date2epoch",
# "src": "%(dateTest2)",
# "format_custom": "(\\d+)-(\\d+)-(\\d+)",
# "year": 1,
# "month": 2,
# "day": 3,
# "timezone": "Europe/Paris",
# "save_epoch": "%(plopDateEpoch)",
# "save_diff1": "%(plopDateDiff1)",
# "save_diff2": "%(plopDateDiff2)"
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|1|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my $tz = {};
$tz->{time_zone} = $options{timezone} if (defined($options{timezone}) && $options{timezone} ne '');
my $dt;
if (defined($options{format_custom}) && $options{format_custom} ne '') {
my @matches = ($data =~ /$options{format_custom}/);
my $date = {};
foreach (('year', 'month', 'day', 'hour', 'minute', 'second')) {
$date->{$_} = $matches[ $options{$_} -1 ]
if (defined($options{$_}) && $options{$_} =~ /^\d+$/ && defined($matches[ $options{$_} -1 ]));
}
foreach (('year', 'month', 'day')) {
if (!defined($date->{$_})) {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} cannot find $_ attribute");
$self->{output}->option_exit();
}
}
$dt = DateTime->new(%$date, %$tz);
} else {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set format_custom attribute");
$self->{output}->option_exit();
}
my $results = {
epoch => $dt->epoch(),
diff1 => time() - $dt->epoch(),
diff2 => $dt->epoch() - time()
};
foreach (keys %$results) {
my $attr = '%(' . $result->{label} . ucfirst($_) . ')';
$attr = $options{'save_' . $_}
if (defined($options{'save_' . $_}) && $options{'save_' . $_} ne '');
my $var_save_value = $self->parse_special_variable(chars => [split //, $attr], start => 0);
if ($var_save_value->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save_$_ attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $results->{$_}, %$var_save_value);
}
}
sub exec_func_epoch2date {
my ($self, %options) = @_;
if (!defined($self->{module_datetime_loaded})) {
centreon::plugins::misc::mymodule_load(
module => 'DateTime',
error_msg => "Cannot load module 'DateTime'."
);
$self->{module_datetime_loaded} = 1;
}
#{
# "type": "epoch2date",
# "src": "%(dateTestEpoch)",
# "format": "%a %b %e %H:%M:%S %Y",
# "timezone": "Asia/Tokyo",
# "locale": "fr",
# "save": "%(dateTestReformat)"
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my $extras = {};
$extras->{time_zone} = $options{timezone} if (defined($options{timezone}) && $options{timezone} ne '');
$extras->{locale} = $options{locale} if (defined($options{locale}) && $options{locale} ne '');
my $dt = DateTime->from_epoch(
epoch => $data,
%$extras
);
my $time_value = $dt->strftime($options{format});
if (defined($options{save}) && $options{save} ne '') {
my $var_save_value = $self->parse_special_variable(chars => [split //, $options{save}], start => 0);
if ($var_save_value->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $time_value, %$var_save_value);
}
}
sub exec_func_count {
my ($self, %options) = @_;
#{
# "type": "count",
# "src": "%(sql.tables.test)",
# "save": "%(testCount)"
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^2$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my $value = 0;
if (defined($data)) {
if (defined($options{filter}) && $options{filter} ne '') {
my $count = 0;
foreach my $instance (keys %$data) {
my $values = $self->{expand};
foreach my $label (keys %{$data->{$instance}}) {
$values->{'src.' . $label} = $data->{$instance}->{$label};
}
$count++ unless ($self->check_filter(filter => $options{filter}, values => $values));
}
$value = $count;
} else {
$value = scalar(keys %$data);
}
}
if (defined($options{save}) && $options{save} ne '') {
my $save = $self->parse_special_variable(chars => [split //, $options{save}], start => 0);
if ($save->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $value, %$save);
}
}
sub exec_func_replace {
my ($self, %options) = @_;
#{
# "type": "replace",
# "src": "%(sql.tables.test)",
# "expression": "s/name/name is/"
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
if (!defined($options{expression}) || $options{expression} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set expression attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
if (defined($data)) {
my $expression = $self->substitute_string(value => $options{expression});
our $assign_var = $data;
$self->{safe_func}->reval("\$assign_var =~ $expression", 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
$self->set_special_variable_value(value => $assign_var, %$result);
}
}
sub exec_func_assign {
my ($self, %options) = @_;
#{
# "type": "assign",
# "save": "%(sql.tables.test)",
# "expression": "'%(sql.tables.test)' . 'toto'"
#}
if (!defined($options{save}) || $options{save} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set save attribute");
$self->{output}->option_exit();
}
if (!defined($options{expression}) || $options{expression} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set expression attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{save}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $expression = $self->substitute_string(value => $options{expression});
our $assign_var;
$self->{safe_func}->reval("\$assign_var = $expression", 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
$self->set_special_variable_value(value => $assign_var, %$result);
}
sub exec_func_capture {
my ($self, %options) = @_;
#{
# "type": "capture",
# "src": "%(snmp.leefs.content)",
# "pattern": "(?msi)Vertical BER Analysis.*?Bit Error Rate: (\S+)",
# "groups": [
# { "offset": 1, "save": "%(bitErrorRate)" }
# ]
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
if (!defined($options{pattern}) || $options{pattern} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set pattern attribute");
$self->{output}->option_exit();
}
if (!defined($options{groups}) || ref($options{groups}) ne 'ARRAY') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set groups attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
my @matches = ($data =~ /$options{pattern}/);
foreach (@{$options{groups}}) {
next if ($_->{offset} !~ /^[0-9]+/);
my $value = '';
if (defined($matches[ $_->{offset} ])) {
$value = $matches[ $_->{offset} ];
}
my $save = $self->parse_special_variable(chars => [split //, $_->{save}], start => 0);
if ($save->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $value, %$save);
}
}
sub exec_func_scientific2number {
my ($self, %options) = @_;
#{
# "type": "scientific2number",
# "src": "%(bitErrorRate)",
# "save": "%(bitErrorRate)",
#}
if (!defined($options{src}) || $options{src} eq '') {
$self->{output}->add_option_msg(short_msg => "$self->{current_section} please set src attribute");
$self->{output}->option_exit();
}
my $result = $self->parse_special_variable(chars => [split //, $options{src}], start => 0);
if ($result->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in src attribute");
$self->{output}->option_exit();
}
my $data = $self->get_special_variable_value(%$result);
$data = centreon::plugins::misc::expand_exponential(value => $data);
if (defined($options{save}) && $options{save} ne '') {
my $save = $self->parse_special_variable(chars => [split //, $options{save}], start => 0);
if ($save->{type} !~ /^(?:0|4)$/) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed in save attribute");
$self->{output}->option_exit();
}
$self->set_special_variable_value(value => $data, %$save);
}
}
sub set_functions {
my ($self, %options) = @_;
return if (!defined($options{functions}));
my $i = -1;
foreach (@{$options{functions}}) {
$i++;
$self->{current_section} = '[' . $options{section} . ' > ' . $i . ']';
next if (defined($_->{position}) && $options{position} ne $_->{position});
next if (!defined($_->{position}) && !(defined($options{default}) && $options{default} == 1));
next if (!defined($_->{type}));
if ($_->{type} eq 'map') {
$self->exec_func_map(%$_);
} elsif ($_->{type} eq 'scale') {
$self->exec_func_scale(%$_);
} elsif ($_->{type} eq 'second2human') {
$self->exec_func_second2human(%$_);
} elsif (lc($_->{type}) eq 'date2epoch') {
$self->exec_func_date2epoch(%$_);
} elsif (lc($_->{type}) eq 'epoch2date') {
$self->exec_func_epoch2date(%$_);
} elsif (lc($_->{type}) eq 'count') {
$self->exec_func_count(%$_);
} elsif (lc($_->{type}) eq 'replace') {
$self->exec_func_replace(%$_);
} elsif (lc($_->{type}) eq 'assign') {
$self->exec_func_assign(%$_);
} elsif (lc($_->{type}) eq 'capture') {
$self->exec_func_capture(%$_);
} elsif (lc($_->{type}) eq 'scientific2number') {
$self->exec_func_scientific2number(%$_);
}
}
}
sub prepare_variables {
my ($self, %options) = @_;
return undef if (!defined($options{value}));
while ($options{value} =~ /%\(([a-zA-Z0-9\.]+?)\)/g) {
next if ($1 =~ /^sql\./);
$options{value} =~ s/%\(($1)\)/\$expand->{'$1'}/g;
}
my $expression = $self->substitute_string(value => $options{value});
return $expression;
}
sub check_filter {
my ($self, %options) = @_;
return 0 if (!defined($options{filter}) || $options{filter} eq '');
our $expand = $options{values};
$options{filter} =~ s/%\(([a-zA-Z0-9\._:]+?)\)/\$expand->{'$1'}/g;
my $result = $self->{safe}->reval("$options{filter}");
if ($@) {
$self->{output}->add_option_msg(short_msg => 'Unsafe code evaluation: ' . $@);
$self->{output}->option_exit();
}
return 0 if ($result);
return 1;
}
sub check_filter_option {
my ($self, %options) = @_;
foreach (keys %{$self->{option_results}->{filter_selection}}) {
return 1 if (
defined($self->{expand}->{$_}) && $self->{option_results}->{filter_selection}->{$_} ne '' &&
$self->{expand}->{$_} !~ /$self->{option_results}->{filter_selection}->{$_}/
);
}
return 0;
}
sub prepare_perfdatas {
my ($self, %options) = @_;
return undef if (!defined($options{perfdatas}));
my $perfdatas = [];
foreach (@{$options{perfdatas}}) {
next if (!defined($_->{nlabel}) || $_->{nlabel} eq '');
next if (!defined($_->{value}) || $_->{value} eq '');
my $perf = {};
$perf->{nlabel} = $self->substitute_string(value => $_->{nlabel});
$perf->{value} = $self->substitute_string(value => $_->{value});
foreach my $label (('warning', 'critical', 'min', 'max', 'unit')) {
next if (!defined($_->{$label}));
$perf->{$label} = $self->substitute_string(value => $_->{$label});
}
if (defined($_->{instances})) {
$perf->{instances} = [];
foreach my $instance (@{$_->{instances}}) {
push @{$perf->{instances}}, $self->substitute_string(value => $instance);
}
}
push @$perfdatas, $perf;
}
return $perfdatas;
}
sub prepare_formatting {
my ($self, %options) = @_;
return undef if (!defined($options{formatting}));
my $format = {};
$format->{printf_msg} = $options{formatting}->{printf_msg};
$format->{display_ok} = $options{formatting}->{display_ok};
if (defined($options{formatting}->{printf_var})) {
$format->{printf_var} = [];
foreach my $var (@{$options{formatting}->{printf_var}}) {
push @{$format->{printf_var}}, $self->substitute_string(value => $var);
}
}
return $format
}
sub add_selection {
my ($self, %options) = @_;
return if (!defined($self->{config}->{selection}));
my $i = -1;
foreach (@{$self->{config}->{selection}}) {
$i++;
my $config = {};
$self->{expand} = $self->set_constants();
$self->set_builtin();
$self->{expand}->{name} = $_->{name} if (defined($_->{name}));
$self->set_functions(section => "selection > $i > functions", functions => $_->{functions}, position => 'before_expand');
$self->set_expand_table(section => "selection > $i > expand_table", expand => $_->{expand_table});
$self->set_expand(section => "selection > $i > expand", expand => $_->{expand});
$self->set_functions(section => "selection > $i > functions", functions => $_->{functions}, position => 'after_expand', default => 1);
next if ($self->check_filter(filter => $_->{filter}, values => $self->{expand}));
next if ($self->check_filter_option());
$config->{unknown} = $self->prepare_variables(section => "selection > $i > unknown", value => $_->{unknown});
$config->{warning} = $self->prepare_variables(section => "selection > $i > warning", value => $_->{warning});
$config->{critical} = $self->prepare_variables(section => "selection > $i > critical", value => $_->{critical});
$config->{perfdatas} = $self->prepare_perfdatas(section => "selection > $i > perfdatas", perfdatas => $_->{perfdatas});
$config->{formatting} = $self->prepare_formatting(section => "selection > $i > formatting", formatting => $_->{formatting});
$config->{formatting_unknown} = $self->prepare_formatting(section => "selection > $i > formatting_unknown", formatting => $_->{formatting_unknown});
$config->{formatting_warning} = $self->prepare_formatting(section => "selection > $i > formatting_warning", formatting => $_->{formatting_warning});
$config->{formatting_critical} = $self->prepare_formatting(section => "selection > $i > formatting_critical", formatting => $_->{formatting_critical});
$self->{selections}->{'s' . $i} = { expand => $self->{expand}, config => $config };
}
}
sub add_selection_loop {
my ($self, %options) = @_;
return if (!defined($self->{config}->{selection_loop}));
my $i = -1;
foreach (@{$self->{config}->{selection_loop}}) {
$i++;
next if (!defined($_->{source}) || $_->{source} eq '');
$self->{current_section} = '[selection_loop > ' . $i . ' > source]';
my $result = $self->parse_special_variable(chars => [split //, $_->{source}], start => 0);
if ($result->{type} != 2) {
$self->{output}->add_option_msg(short_msg => $self->{current_section} . " special variable type not allowed");
$self->{output}->option_exit();
}
next if (!defined($self->{sql_collected}->{tables}->{ $result->{table} }));
foreach my $instance (keys %{$self->{sql_collected}->{tables}->{ $result->{table} }}) {
$self->{expand} = $self->set_constants();
$self->set_builtin();
$self->{expand}->{ $result->{table} . '.instance' } = $instance;
foreach my $label (keys %{$self->{sql_collected}->{tables}->{ $result->{table} }->{$instance}}) {
$self->{expand}->{ $result->{table} . '.' . $label } =
$self->{sql_collected}->{tables}->{ $result->{table} }->{$instance}->{$label};
}
my $config = {};
$self->{expand}->{name} = $_->{name} if (defined($_->{name}));
$self->set_functions(section => "selection_loop > $i > functions", functions => $_->{functions}, position => 'before_expand');
$self->set_expand_table(section => "selection_loop > $i > expand_table", expand => $_->{expand_table});
$self->set_expand(section => "selection_loop > $i > expand", expand => $_->{expand});
$self->set_functions(section => "selection_loop > $i > functions", functions => $_->{functions}, position => 'after_expand', default => 1);
next if ($self->check_filter(filter => $_->{filter}, values => $self->{expand}));
next if ($self->check_filter_option());
$config->{unknown} = $self->prepare_variables(section => "selection_loop > $i > unknown", value => $_->{unknown});
$config->{warning} = $self->prepare_variables(section => "selection_loop > $i > warning", value => $_->{warning});
$config->{critical} = $self->prepare_variables(section => "selection_loop > $i > critical", value => $_->{critical});
$config->{perfdatas} = $self->prepare_perfdatas(section => "selection_loop > $i > perfdatas", perfdatas => $_->{perfdatas});
$config->{formatting} = $self->prepare_formatting(section => "selection_loop > $i > formatting", formatting => $_->{formatting});
$config->{formatting_unknown} = $self->prepare_formatting(section => "selection_loop > $i > formatting_unknown", formatting => $_->{formatting_unknown});
$config->{formatting_warning} = $self->prepare_formatting(section => "selection_loop > $i > formatting_warning", formatting => $_->{formatting_warning});
$config->{formatting_critical} = $self->prepare_formatting(section => "selection_loop > $i > formatting_critical", formatting => $_->{formatting_critical});
$self->{selections}->{'s' . $i . '-' . $instance} = { expand => $self->{expand}, config => $config };
}
}
}
sub set_formatting {
my ($self, %options) = @_;
return if (!defined($self->{config}->{formatting}));
if (defined($self->{config}->{formatting}->{custom_message_global})) {
$self->{maps_counters_type}->[0]->{message_multiple} = $self->{config}->{formatting}->{custom_message_global};
}
if (defined($self->{config}->{formatting}->{separator})) {
$self->{maps_counters_type}->[0]->{message_separator} = $self->{config}->{formatting}->{separator};
}
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['name']);
}
sub disco_show {
my ($self, %options) = @_;
$self->read_config();
$self->collect_sql(sql => $options{sql});
$self->{selections} = {};
$self->add_selection();
$self->add_selection_loop();
foreach (values %{$self->{selections}}) {
my $entry = {};
foreach my $label (keys %{$_->{expand}}) {
next if ($label =~ /^(?:constants|builtin)\./);
my $name = $label;
$name =~ s/\./_/g;
$entry->{$name} = defined($_->{expand}->{$label}) ? $_->{expand}->{$label} : '';
}
$self->{output}->add_disco_entry(%$entry);
}
}
sub manage_selection {
my ($self, %options) = @_;
$self->read_config();
$self->collect_sql(sql => $options{sql});
$self->{selections} = {};
$self->add_selection();
$self->add_selection_loop();
$self->set_formatting();
}
1;
=head1 MODE
Collect and compute SQL data.
=over 8
=item B<--config>
config used (required).
Can be a file or json content.
=item B<--filter-selection>
Filter selections.
Example: --filter-selection='name=test'
=item B<--constant>
Add a constant.
Example: --constant='warning=30' --constant='critical=45'
=back
=cut
CENTREON_COMMON_PROTOCOLS_SQL_MODE_COLLECTION
$fatpacked{"centreon/common/protocols/sql/mode/connectiontime.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_COMMON_PROTOCOLS_SQL_MODE_CONNECTIONTIME';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::protocols::sql::mode::connectiontime;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use Time::HiRes;
use POSIX;
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 => {
'warning:s' => { name => 'warning', },
'critical:s' => { name => 'critical' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
my $now = Time::HiRes::time();
my ($exit, $msg_error) = $self->{sql}->connect(dontquit => 1);
my $now2 = Time::HiRes::time();
$self->{sql}->disconnect();
if ($exit == -1) {
$self->{output}->output_add(
severity => 'CRITICAL',
short_msg => $msg_error
);
} else {
my $milliseconds = $now2 - $now;
$milliseconds = floor($milliseconds * 1000);
my $exit_code = $self->{perfdata}->threshold_check(value => $milliseconds, threshold => [ { label => 'critical', 'exit_litteral' => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf("Connection established in %.3fs.", $milliseconds / 1000)
);
$self->{output}->perfdata_add(
label => 'connection_time',
nlabel => 'connection.time.milliseconds',
value => $milliseconds,
unit => 'ms',
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check database connection time.
=over 8
=item B<--warning>
Warning threshold in milliseconds.
=item B<--critical>
Critical threshold in milliseconds.
=back
=cut
CENTREON_COMMON_PROTOCOLS_SQL_MODE_CONNECTIONTIME
$fatpacked{"centreon/common/protocols/sql/mode/sql.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_COMMON_PROTOCOLS_SQL_MODE_SQL';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::protocols::sql::mode::sql;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use Time::HiRes qw(gettimeofday tv_interval);
sub custom_value_output {
my ($self, %options) = @_;
return sprintf($self->{instance_mode}->{option_results}->{format}, $self->{result_values}->{value});
}
sub custom_value_perfdata {
my ($self, %options) = @_;
$self->{output}->perfdata_add(
nlabel => $self->{instance_mode}->{option_results}->{perfdata_name},
unit => $self->{instance_mode}->{option_results}->{perfdata_unit},
value => $self->{result_values}->{value},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-' . $self->{thlabel}),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-' . $self->{thlabel}),
min => $self->{instance_mode}->{option_results}->{perfdata_min},
max => $self->{instance_mode}->{option_results}->{perfdata_max}
);
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0 }
];
$self->{maps_counters}->{global} = [
{ label => 'value', set => {
key_values => [ { name => 'value' } ],
closure_custom_output => $self->can('custom_value_output'),
closure_custom_perfdata => $self->can('custom_value_perfdata')
}
},
{ label => 'execution-time', nlabel => 'sqlrequest.execution.time.seconds', display_ok => 0, set => {
key_values => [ { name => 'time' } ],
output_template => 'execution time: %.3f second(s)',
perfdatas => [
{ template => '%.3f', min => 0, unit => 's' }
]
}
}
];
}
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 => {
'sql-statement:s' => { name => 'sql_statement' },
'format:s' => { name => 'format', default => 'SQL statement result : %i.' },
'perfdata-unit:s' => { name => 'perfdata_unit', default => '' },
'perfdata-name:s' => { name => 'perfdata_name', default => 'value' },
'perfdata-min:s' => { name => 'perfdata_min', default => '' },
'perfdata-max:s' => { name => 'perfdata_max', default => '' },
'warning:s' => { name => 'warning', redirect => 'warning-value' },
'critical:s' => { name => 'critical', redirect => 'critical-value' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (!defined($self->{option_results}->{sql_statement}) || $self->{option_results}->{sql_statement} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify '--sql-statement' option.");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{format}) || $self->{option_results}->{format} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify '--format' option.");
$self->{output}->option_exit();
}
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
my $timing0 = [gettimeofday];
$options{sql}->query(query => $self->{option_results}->{sql_statement});
my $value = $options{sql}->fetchrow_array();
$self->{global} = {
value => $value,
time => tv_interval($timing0, [gettimeofday])
};
$options{sql}->disconnect();
}
1;
=head1 MODE
Check SQL statement.
=over 8
=item B<--sql-statement>
SQL statement that returns a number.
=item B<--format>
Output format (default: 'SQL statement result : %i.').
=item B<--perfdata-unit>
Perfdata unit in perfdata output (default: '')
=item B<--perfdata-name>
Perfdata name in perfdata output (default: 'value')
=item B<--perfdata-min>
Minimum value to add in perfdata output (default: '')
=item B<--perfdata-max>
Maximum value to add in perfdata output (default: '')
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'value', 'execution-time'.
=back
=cut
CENTREON_COMMON_PROTOCOLS_SQL_MODE_SQL
$fatpacked{"centreon/common/protocols/sql/mode/sqlstring.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_COMMON_PROTOCOLS_SQL_MODE_SQLSTRING';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::common::protocols::sql::mode::sqlstring;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'rows', type => 1, message_multiple => "SQL Query is OK" },
];
$self->{maps_counters}->{rows} = [
{ label => 'string', type => 2, set => {
key_values => [ { name => 'key_field' }, { name => 'value_field' } ],
closure_custom_output => $self->can('custom_string_output'),
closure_custom_threshold_check => \&catalog_status_threshold_ng,
closure_custom_perfdata => sub { return 0; }
}
}
];
}
sub custom_string_output {
my ($self, %options) = @_;
my $msg;
my $message;
if (defined($self->{instance_mode}->{option_results}->{printf_format}) && $self->{instance_mode}->{option_results}->{printf_format} ne '') {
eval {
local $SIG{__WARN__} = sub { $message = $_[0]; };
local $SIG{__DIE__} = sub { $message = $_[0]; };
$msg = sprintf("$self->{instance_mode}->{option_results}->{printf_format}", $self->{result_values}->{ $self->{instance_mode}->{printf_value} });
};
} else {
$msg = sprintf("'%s'", $self->{result_values}->{value_field});
}
if (defined($message)) {
$self->{output}->output_add(long_msg => 'output value issue: ' . $message);
}
return $msg;
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'sql-statement:s' => { name => 'sql_statement' },
'key-column:s' => { name => 'key_column' },
'value-column:s' => { name => 'value_column' },
'printf-format:s' => { name => 'printf_format' },
'printf-value:s' => { name => 'printf_value' },
'dual-table' => { name => 'dual_table' },
'empty-sql-string:s' => { name => 'empty_sql_string', default => 'No row returned or --key-column/--value-column do not correctly match selected field' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
if (!defined($self->{option_results}->{sql_statement}) || $self->{option_results}->{sql_statement} eq '') {
$self->{output}->add_option_msg(short_msg => "Need to specify '--sql-statement' option.");
$self->{output}->option_exit();
}
$self->{printf_value} = 'value_field';
if (defined($self->{option_results}->{printf_value}) && $self->{option_results}->{printf_value} ne '') {
$self->{printf_value} = $1
if ($self->{option_results}->{printf_value} =~ /\$self->\{result_values\}->\{(value_field|key_field)\}/);
$self->{printf_value} = $1
if ($self->{option_results}->{printf_value} =~ /\%\{(value_field|key_field)\}/);
$self->{printf_value} = $1
if ($self->{option_results}->{printf_value} =~ /\%\((value_field|key_field)\)/);
}
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
$options{sql}->query(query => $self->{option_results}->{sql_statement});
$self->{rows} = {};
my $row_count = 0;
while (my $row = $options{sql}->fetchrow_hashref()) {
if (defined($self->{option_results}->{dual_table})) {
$row->{$self->{option_results}->{value_column}} = delete $row->{keys %{$row}};
foreach (keys %{$row}) {
$row->{$self->{option_results}->{value_column}} = $row->{$_};
}
}
if (!defined($self->{option_results}->{key_column})) {
$self->{rows}->{$self->{option_results}->{value_column} . $row_count} = {
key_field => $row->{ $self->{option_results}->{value_column} },
value_field => $row->{ $self->{option_results}->{value_column} }
};
$row_count++;
} else {
$self->{rows}->{$self->{option_results}->{key_column} . $row_count} = {
key_field => $row->{ $self->{option_results}->{key_column} },
value_field => $row->{ $self->{option_results}->{value_column} }
};
$row_count++;
}
}
$options{sql}->disconnect();
if (scalar(keys %{$self->{rows}}) <= 0) {
$self->{output}->add_option_msg(short_msg => $self->{option_results}->{empty_sql_string});
$self->{output}->option_exit();
}
}
1;
=head1 MODE
Check SQL statement to query string pattern (You cannot have more than to fiels in select)
=over 8
=item B<--sql-statement>
SQL statement that returns a string.
=item B<--key-column>
Key column (must be one of the selected field). NOT mandatory if you select only one field
=item B<--value-column>
Value column (must be one of the selected field). MANDATORY
=item B<--printf-format>
Specify a custom output message relying on printf formatting. If this option is set --printf-value is mandatory.
=item B<--printf-value>
Specify variable used to replace in printf. If this option is set --printf-format is mandatory.
Can be: %{key_field} (default value) or %{value_field}
=item B<--warning-string>
Define the conditions to match for the status to be WARNING (can be %{key_field}, %{value_field}).
Example: --warning-string '%{key_field} eq 'Central' && %{value_field} =~ /127.0.0.1/'
=item B<--critical-string>
Define the conditions to match for the status to be CRITICAL
(can be %{key_field} or %{value_field})
=item B<--dual-table>
Set this option to ensure compatibility with dual table and Oracle.
=item B<--empty-sql-string>
Set this option to change the output message when the sql statement result is empty.
(default: 'No row returned or --key-column/--value-column do not correctly match selected field')
=back
=cut
CENTREON_COMMON_PROTOCOLS_SQL_MODE_SQLSTRING
$fatpacked{"centreon/plugins/alternative/FatPackerOptions.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_ALTERNATIVE_FATPACKEROPTIONS';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::alternative::FatPackerOptions;
use base qw(centreon::plugins::options);
use strict;
use warnings;
use Pod::Usage;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
return $self;
}
sub display_help {
my ($self, %options) = @_;
my $stdout;
foreach (@{$self->{pod_package}}) {
{
my $pp = $_->{package} . ".pm";
$pp =~ s{::}{/}g;
my $content_class = $INC{$pp}->{$pp};
open my $str_fh, '<', \$content_class;
local *STDOUT;
open STDOUT, '>', \$stdout;
pod2usage(-exitval => 'NOEXIT', -input => $str_fh,
-verbose => 99,
-sections => $_->{sections});
close $str_fh;
}
$self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
}
}
1;
CENTREON_PLUGINS_ALTERNATIVE_FATPACKEROPTIONS
$fatpacked{"centreon/plugins/alternative/Getopt.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_ALTERNATIVE_GETOPT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::alternative::Getopt;
use strict;
use warnings;
use Exporter;
use vars qw(@ISA @EXPORT @EXPORT_OK);
@ISA = qw(Exporter);
BEGIN {
@EXPORT = qw(&GetOptions);
@EXPORT_OK = qw();
}
use vars @EXPORT, @EXPORT_OK;
our $warn_message = 0;
sub get_assigned_value {
my (%options) = @_;
if (!defined($options{val}) || $options{val} eq '') {
# Add defined also. Hardened code: already see: $ARGV[6] = undef for example
if ($options{pos} + 1 < $options{num_args} && defined($ARGV[$options{pos} + 1]) && $ARGV[$options{pos} + 1] !~ /^--/) {
my $val = $ARGV[$options{pos} + 1];
splice @ARGV, $options{pos} + 1, 1;
return ($options{num_args} - 1, $val);
} else {
return ($options{num_args}, '');
}
}
return ($options{num_args}, $options{val});
}
sub GetOptions {
my (%opts) = @_;
my $search_str = ',' . join(',', keys %opts) . ',';
my $num_args = scalar(@ARGV);
for (my $i = 0; $i < $num_args;) {
if (defined($ARGV[$i]) && $ARGV[$i] =~ /^--(.*?)(?:=|$)((?s).*)/) {
my ($option, $value) = ($1, $2);
# The special argument "--" forces an end of option-scanning.
# All arguments placed after are stored in a list with the special option key '_double_dash_'.
if ($option eq '' && $value eq '') {
my @values = splice @ARGV, $i + 1, $num_args - $i - 1;
push @{${$opts{'_double_dash_'}}}, @values;
splice @ARGV, $i, 1;
last;
}
# find type of option
if ($search_str !~ /,((?:[^,]*?\|){0,}$option(?:\|.*?){0,}(:.*?){0,1}),/) {
# for old format plugins (with run function) that not allowed list-counters options
if($option =~ /list-counters/){
warn "list-counters option not available yet for this mode." if ($warn_message == 1);
}else{
warn "Unknown option: $option" if ($warn_message == 1);
}
$i++;
next;
}
my ($option_selected, $type_opt) = ($1, $2);
if (!defined($type_opt)) {
($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
if ($assigned ne '0') {
${$opts{$option_selected}} = 1;
}
} elsif ($type_opt =~ /:s$/) {
($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
${$opts{$option_selected}} = $assigned;
} elsif ($type_opt =~ /:s\@$/) {
${$opts{$option . $type_opt}} = [] if (!defined(${$opts{$option . $type_opt}}));
($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
push @{${$opts{$option_selected}}}, $assigned;
} elsif ($type_opt =~ /:s\%$/) {
${$opts{$option . $type_opt}} = {} if (!defined(${$opts{$option . $type_opt}}));
($num_args, my $assigned) = get_assigned_value(num_args => $num_args, pos => $i, val => $value);
if ($assigned =~ /^(.*?)=(.*)/) {
${$opts{$option_selected}}->{$1} = $2;
}
}
splice @ARGV, $i, 1;
$num_args--;
} else {
warn "argument $ARGV[$i] alone" if ($warn_message == 1 && $i != 0 && defined($ARGV[$i]));
$i++;
}
}
}
1;
CENTREON_PLUGINS_ALTERNATIVE_GETOPT
$fatpacked{"centreon/plugins/backend/http/curl.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_CURL';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::backend::http::curl;
use strict;
use warnings;
use URI;
use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{noptions}) || $options{noptions} != 1) {
$options{options}->add_options(arguments => {
'curl-opt:s@' => { name => 'curl_opt' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND CURL OPTIONS', once => 1);
}
$self->{output} = $options{output};
$self->{curl_log} = $options{curl_logger};
return $self;
}
sub check_options {
my ($self, %options) = @_;
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Net::Curl::Easy',
error_msg => "Cannot load module 'Net::Curl::Easy'."
);
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'centreon::plugins::backend::http::curlconstants',
error_msg => "Cannot load module 'centreon::plugins::backend::http::curlconstants'."
);
$self->{constant_cb} = \¢reon::plugins::backend::http::curlconstants::get_constant_value;
foreach (('unknown_status', 'warning_status', 'critical_status')) {
if (defined($options{request}->{$_})) {
$options{request}->{$_} =~ s/%\{http_code\}/\$values->{code}/g;
}
}
if (!defined($options{request}->{curl_opt})) {
$options{request}->{curl_opt} = [];
}
}
my $http_code_explained = {
100 => 'Continue',
101 => 'Switching Protocols',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => '(Unused)',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
450 => 'Timeout reached', # custom code
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported'
};
sub cb_debug {
my ($easy, $type, $data, $uservar) = @_;
my $msg = '';
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_TEXT')) {
$msg = sprintf("== Info: %s", $data);
}
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_OUT')) {
$msg = sprintf("=> Send header: %s", $data);
}
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_OUT')) {
$msg = sprintf("=> Send data: %s", $data);
}
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_HEADER_IN')) {
$msg = sprintf("=> Recv header: %s", $data);
}
if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_DATA_IN')) {
$msg = sprintf("=> Recv data: %s", $data);
}
return 0 if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_OUT'));
return 0 if ($type == $uservar->{constant_cb}->(name => 'CURLINFO_SSL_DATA_IN'));
$uservar->{output}->output_add(long_msg => $msg, debug => 1);
return 0;
}
sub curl_setopt {
my ($self, %options) = @_;
eval {
$self->{curl_easy}->setopt($options{option}, $options{parameter});
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "curl setopt error: '" . $@ . "'.");
$self->{output}->option_exit();
}
}
sub set_method {
my ($self, %options) = @_;
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => undef);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => undef);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPGET'), parameter => 1);
my $skip_log_post = 0;
# POST inferred by CURLOPT_POSTFIELDS
if ($options{content_type_forced} == 1) {
if (defined($options{request}->{query_form_post})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $options{request}->{query_form_post});
$self->{curl_log}->log("--data-raw", $options{request}->{query_form_post});
$skip_log_post = 1;
}
} elsif (defined($options{request}->{post_params})) {
my $uri_post = URI->new();
$uri_post->query_form($options{request}->{post_params});
my $urlencodedheader = 'Content-Type: application/x-www-form-urlencoded';
push @{$options{headers}}, $urlencodedheader;
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POSTFIELDS'), parameter => $uri_post->query);
$self->{curl_log}->log("-H", $urlencodedheader);
$self->{curl_log}->log("--data-raw", $uri_post->query);
$skip_log_post = 1;
}
if ($options{request}->{method} eq 'GET') {
# no curl_log call because GET is the default value
return;
}
if ($options{request}->{method} eq 'POST') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_POST'), parameter => 1);
$self->{curl_log}->log('-X', $options{request}->{method}) unless $skip_log_post;
return;
}
$self->{curl_log}->log('-X', $options{request}->{method});
if ($options{request}->{method} eq 'PUT') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
}
if ($options{request}->{method} eq 'DELETE') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
}
if ($options{request}->{method} eq 'PATCH') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CUSTOMREQUEST'), parameter => $options{request}->{method});
}
}
sub set_auth {
my ($self, %options) = @_;
if (defined($options{request}->{credentials})) {
if (defined($options{request}->{basic})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_BASIC'));
$self->{curl_log}->log('--basic');
} elsif (defined($options{request}->{ntlmv2})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_NTLM'));
$self->{curl_log}->log('--ntlm');
} elsif (defined($options{request}->{digest})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_DIGEST'));
$self->{curl_log}->log('--digest');
}else {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPAUTH'), parameter => $self->{constant_cb}->(name => 'CURLAUTH_ANY'));
$self->{curl_log}->log('--anyauth');
}
my $userpassword = $options{request}->{username} . ':' . $options{request}->{password};
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_USERPWD'), parameter => $userpassword);
$self->{curl_log}->log('--user', $userpassword);
}
if (defined($options{request}->{cert_file}) && $options{request}->{cert_file} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERT'), parameter => $options{request}->{cert_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLKEY'), parameter => $options{request}->{key_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_KEYPASSWD'), parameter => $options{request}->{cert_pwd});
$self->{curl_log}->log('--cert', $options{request}->{cert_file});
$self->{curl_log}->log('--key', $options{request}->{key_file});
$self->{curl_log}->log('--pass', $options{request}->{cert_pwd}) if defined $options{request}->{cert_pwd} && $options{request}->{cert_pwd} ne '';
}
if (defined($options{request}->{cert_pkcs12})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "P12");
$self->{curl_log}->log('--cert-type', 'p12');
} else {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSLCERTTYPE'), parameter => "PEM");
# no curl_log call because PEM is the default value
}
}
sub set_form {
my ($self, %options) = @_;
if (!defined($self->{form_loaded})) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Net::Curl::Form',
error_msg => "Cannot load module 'Net::Curl::Form'."
);
$self->{form_loaded} = 1;
}
my $form = Net::Curl::Form->new();
foreach (@{$options{form}}) {
my %args = ();
$args{ $self->{constant_cb}->(name => 'CURLFORM_COPYNAME()') } = $_->{copyname}
if (defined($_->{copyname}));
$args{ $self->{constant_cb}->(name => 'CURLFORM_COPYCONTENTS()') } = $_->{copycontents}
if (defined($_->{copycontents}));
$form->add(%args);
$self->{curl_log}->log("--form-string", $_->{copyname}."=".$_->{copycontents})
if defined($_->{copyname}) && defined($_->{copycontents});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HTTPPOST()'), parameter => $form);
}
sub set_proxy {
my ($self, %options) = @_;
if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_PROXY'), parameter => $options{request}->{proxyurl});
$self->{curl_log}->log("--proxy", $options{request}->{proxyurl});
}
if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
$self->{output}->add_option_msg(short_msg => 'Unsupported proxypac option');
$self->{output}->option_exit();
}
}
sub set_extra_curl_opt {
my ($self, %options) = @_;
my $entries = {};
foreach (@{$options{request}->{curl_opt}}) {
my ($key, $value) = split /=>/;
$key = centreon::plugins::misc::trim($key);
if (!defined($entries->{$key})) {
$entries->{$key} = { val => [], constants => [], force_array => 0 };
}
$value = centreon::plugins::misc::trim($value);
# Here we want to convert a string containing curl options into a single value or into
# an array of values depending on whether it begins with '[' and ends with ']'.
# We also remove the quotes.
# for example:
#
# $opt = ["CURLOPT_SSL_VERIFYPEER =>[opt1,'opt2','opt3']"];
# is converted to a Perl array like this:
# $VAR1 = [
# 'opt1',
# 'opt2',
# 'opt3'
# ];
#
# $opt = [ "CURLOPT_SSL_VERIFYPEER => 'opt1'" ];
# is converted to:
# $VAR1 = 'opt1';
if ($value =~ /^\[(.*)\]$/) {
$entries->{$key}->{force_array} = 1;
$value = centreon::plugins::misc::trim($1);
push @{$entries->{$key}->{constants}}, map { $_ = centreon::plugins::misc::trim($_); s/^'(.*)'$/$1/; $_ } split ',', $value;
} else {
push @{$entries->{$key}->{constants}}, $value =~ /^'(.*)'$/ ? $1 : $value;
}
if ($value =~ /^CURLOPT|CURL/) {
$value = $self->{constant_cb}->(name => $value);
}
push @{$entries->{$key}->{val}}, $value;
}
foreach (keys %$entries) {
my $key = $_;
if ($self->{curl_log}->is_enabled()) {
$self->{curl_log}->convert_curlopt_to_cups_parameter(
key => $key,
parameter => $entries->{$key}->{constants},
);
}
if (/^CURLOPT|CURL/) {
$key = $self->{constant_cb}->(name => $_);
}
my $parameter;
if ($entries->{$_}->{force_array} == 1 || scalar(@{$entries->{$_}->{val}}) > 1) {
$parameter = $entries->{$_}->{val};
} else {
$parameter = pop @{$entries->{$_}->{val}};
}
$self->curl_setopt(option => $key, parameter => $parameter);
}
}
sub cb_get_header {
my ($easy, $header, $uservar) = @_;
$header =~ s/[\r\n]//g;
if ($header =~ /^[\r\n]*$/) {
$uservar->{nheaders}++;
} else {
$uservar->{response_headers}->[$uservar->{nheaders}] = {}
if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]));
if ($header =~ /^(\S(?:.*?))\s*:\s*(.*)/) {
my $header_name = lc($1);
$uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name} = []
if (!defined($uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}));
push @{$uservar->{response_headers}->[$uservar->{nheaders}]->{$header_name}}, $2;
} else {
$uservar->{response_headers}->[$uservar->{nheaders}]->{response_line} = $header;
}
}
return length($_[1]);
}
sub request {
my ($self, %options) = @_;
# Enable curl logger when debug mode is on
$self->{curl_log}->init( enabled => $self->{output}->is_debug() );
if (!defined($self->{curl_easy})) {
$self->{curl_easy} = Net::Curl::Easy->new();
}
my $url;
if (defined($options{request}->{full_url})) {
$url = $options{request}->{full_url};
} elsif (defined($options{request}->{port}) && $options{request}->{port} =~ /^[0-9]+$/) {
$url = $options{request}->{proto}. "://" . $options{request}->{hostname} . ':' . $options{request}->{port} . $options{request}->{url_path};
} else {
$url = $options{request}->{proto}. "://" . $options{request}->{hostname} . $options{request}->{url_path};
}
my $uri = URI->new($url);
if (defined($options{request}->{get_params})) {
$uri->query_form($options{request}->{get_params});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_URL'), parameter => $uri);
$self->{curl_log}->log($uri);
if ($self->{output}->is_debug()) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGFUNCTION'), parameter => \&cb_debug);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_DEBUGDATA'), parameter => $self);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_VERBOSE'), parameter => 1);
$self->{curl_log}->log('--verbose');
}
if (defined($options{request}->{timeout}) && $options{request}->{timeout} =~ /\d/) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_TIMEOUT'), parameter => $options{request}->{timeout});
$self->{curl_log}->log("--max-time", $options{request}->{timeout});
}
if (defined($options{request}->{cookies_file})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEFILE'), parameter => $options{request}->{cookies_file});
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_COOKIEJAR'), parameter => $options{request}->{cookies_file});
$self->{curl_log}->log('--cookie', $options{request}->{cookies_file});
$self->{curl_log}->log('--cookie-jar', $options{request}->{cookies_file});
}
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 1);
if (defined($options{request}->{no_follow})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FOLLOWLOCATION'), parameter => 0);
} else {
$self->{curl_log}->log('-L');
}
if (defined($options{request}->{http_peer_addr}) && $options{request}->{http_peer_addr} ne '') {
$url =~ /^(?:http|https):\/\/(.*?)(\/|\:|$)/;
my $resolve = $1 . ':' . $options{request}->{port_force} . ':' . $options{request}->{http_peer_addr};
$self->{curl_easy}->setopt(
$self->{constant_cb}->(name => 'CURLOPT_RESOLVE'),
[$resolve]
);
$self->{curl_log}->log('--resolve', $resolve);
}
my $headers = [];
my $content_type_forced = 0;
foreach my $key (keys %{$options{request}->{headers}}) {
my $header = $key . ':' . (defined($options{request}->{headers}->{$key}) ? $options{request}->{headers}->{$key} : '');
push @$headers, $header;
if ($key =~ /content-type/i) {
$content_type_forced = 1;
}
$self->{curl_log}->log("-H", $header);
}
$self->set_method(%options, content_type_forced => $content_type_forced, headers => $headers);
if (defined($options{request}->{form})) {
$self->set_form(form => $options{request}->{form});
}
if (scalar(@$headers) > 0) {
$self->{curl_easy}->setopt($self->{constant_cb}->(name => 'CURLOPT_HTTPHEADER'), $headers);
}
if (defined($options{request}->{cacert_file}) && $options{request}->{cacert_file} ne '') {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CAINFO'), parameter => $options{request}->{cacert_file});
$self->{curl_log}->log('--cacert', $options{request}->{cacert_file});
}
if (defined($options{request}->{insecure})) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYPEER'), parameter => 0);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_SSL_VERIFYHOST'), parameter => 0);
$self->{curl_log}->log('--insecure');
}
$self->set_auth(%options);
$self->set_proxy(%options);
$self->set_extra_curl_opt(%options);
$self->{response_body} = '';
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_FILE'), parameter => \$self->{response_body});
$self->{nheaders} = 0;
$self->{response_headers} = [{}];
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERDATA'), parameter => $self);
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_HEADERFUNCTION'), parameter => \&cb_get_header);
if (defined($options{request}->{certinfo}) && $options{request}->{certinfo} == 1) {
$self->curl_setopt(option => $self->{constant_cb}->(name => 'CURLOPT_CERTINFO'), parameter => 1);
# no curl_log call because there is no equivalent in command line
}
$self->{response_code} = undef;
if ($self->{curl_log}->is_enabled()) {
$self->{output}->output_add(long_msg => 'curl request [curl backend]: ' . $self->{curl_log}->get_log());
}
eval {
$self->{curl_easy}->perform();
};
if ($@) {
my $err = $@;
if (ref($@) eq "Net::Curl::Easy::Code") {
my $num = $@;
if ($num == $self->{constant_cb}->(name => 'CURLE_OPERATION_TIMEDOUT')) {
$self->{response_code} = 450;
}
}
if (!defined($self->{response_code})) {
$self->{output}->add_option_msg(short_msg => 'curl perform error : ' . $err);
$self->{output}->option_exit();
}
}
$self->{response_code} = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_RESPONSE_CODE'))
if (!defined($self->{response_code}));
# Check response
my $status = 'ok';
if (defined($options{request}->{critical_status}) && $options{request}->{critical_status} ne '' &&
$self->{output}->test_eval(test => $options{request}->{critical_status}, values => { code => $self->{response_code} })) {
$status = 'critical';
} elsif (defined($options{request}->{warning_status}) && $options{request}->{warning_status} ne '' &&
$self->{output}->test_eval(test => $options{request}->{warning_status}, values => { code => $self->{response_code} })) {
$status = 'warning';
} elsif (defined($options{request}->{unknown_status}) && $options{request}->{unknown_status} ne '' &&
$self->{output}->test_eval(test => $options{request}->{unknown_status}, values => { code => $self->{response_code} })) {
$status = 'unknown';
}
if (!$options{request}->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
my $short_msg = $self->{response_code} . ' ' .
(defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'unknown');
$self->{output}->output_add(
severity => $status,
short_msg => $short_msg
);
$self->{output}->display();
$self->{output}->exit();
}
return $self->{response_body};
}
sub get_headers {
my ($self, %options) = @_;
my $headers = '';
foreach (keys %{$self->{response_headers}->[$options{nheader}]}) {
next if (/response_line/);
foreach my $value (@{$self->{response_headers}->[$options{nheader}]->{$_}}) {
$headers .= "$_: " . $value . "\n";
}
}
return $headers;
}
sub get_first_header {
my ($self, %options) = @_;
if (!defined($options{name})) {
return $self->get_headers(nheader => 0);
}
return undef
if (!defined($self->{response_headers}->[0]->{ lc($options{name}) }));
return wantarray ? @{$self->{response_headers}->[0]->{ lc($options{name}) }} : $self->{response_headers}->[0]->{ lc($options{name}) }->[0];
}
sub get_header {
my ($self, %options) = @_;
if (!defined($options{name})) {
return $self->get_headers(nheader => -1);
}
return undef
if (!defined($self->{response_headers}->[-1]->{ lc($options{name}) }));
return wantarray ? @{$self->{response_headers}->[-1]->{ lc($options{name}) }} : $self->{response_headers}->[-1]->{ lc($options{name}) }->[0];
}
sub get_code {
my ($self, %options) = @_;
return $self->{response_code};
}
sub get_message {
my ($self, %options) = @_;
return defined($http_code_explained->{$self->{response_code}}) ? $http_code_explained->{$self->{response_code}} : 'Unknown code';
}
sub get_certificate {
my ($self, %options) = @_;
my $certs = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_CERTINFO'));
return ('pem', $certs->[0]->{Cert});
}
sub get_times {
my ($self, %options) = @_;
# TIME_T = 7.61.0
my $resolve = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_NAMELOOKUP_TIME'));
my $connect = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_CONNECT_TIME'));
my $appconnect = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_APPCONNECT_TIME'));
my $start = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_STARTTRANSFER_TIME'));
my $total = $self->{curl_easy}->getinfo($self->{constant_cb}->(name => 'CURLINFO_TOTAL_TIME'));
my $times = {
resolve => $resolve * 1000,
connect => ($connect - $resolve) * 1000,
transfer => ($total - $start) * 1000
};
if ($appconnect > 0) {
$times->{tls} = ($appconnect - $connect) * 1000;
$times->{processing} = ($start - $appconnect) * 1000;
} else {
$times->{processing} = ($start - $connect) * 1000;
}
return $times;
}
1;
=head1 NAME
HTTP Curl backend layer.
=head1 SYNOPSIS
HTTP Curl backend layer.
=head1 BACKEND CURL OPTIONS
=over 8
=item B<--curl-opt>
Set CURL Options (--curl-opt="CURLOPT_SSL_VERIFYPEER => 0" --curl-opt="CURLOPT_SSLVERSION => CURL_SSLVERSION_TLSv1_1" ).
=back
=head1 DESCRIPTION
B<http>.
=cut
CENTREON_PLUGINS_BACKEND_HTTP_CURL
$fatpacked{"centreon/plugins/backend/http/curlconstants.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_CURLCONSTANTS';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::backend::http::curlconstants;
use strict;
use warnings;
use Net::Curl::Easy qw(:constants);
use Net::Curl::Form qw(:constants);
sub get_constant_value {
my (%options) = @_;
return eval $options{name};
}
1;
CENTREON_PLUGINS_BACKEND_HTTP_CURLCONSTANTS
$fatpacked{"centreon/plugins/backend/http/lwp.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_LWP';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::backend::http::lwp;
use strict;
use warnings;
use centreon::plugins::backend::http::useragent;
use URI;
use IO::Socket::SSL;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{noptions}) || $options{noptions} != 1) {
$options{options}->add_options(arguments => {
'ssl:s' => { name => 'ssl' },
'ssl-opt:s@' => { name => 'ssl_opt' },
});
$options{options}->add_help(package => __PACKAGE__, sections => 'BACKEND LWP OPTIONS', once => 1);
}
$self->{output} = $options{output};
$self->{ua} = undef;
$self->{debug_handlers} = 0;
return $self;
}
sub check_options {
my ($self, %options) = @_;
foreach (('unknown_status', 'warning_status', 'critical_status')) {
if (defined($options{request}->{$_})) {
$options{request}->{$_} =~ s/%\{http_code\}/\$values->{code}/g;
}
}
$self->{ssl_context} = '';
if (!defined($options{request}->{ssl_opt})) {
$options{request}->{ssl_opt} = [];
}
if (defined($options{request}->{ssl}) && $options{request}->{ssl} ne '') {
push @{$options{request}->{ssl_opt}}, 'SSL_version => ' . $options{request}->{ssl};
}
if (defined($options{request}->{cert_file}) && !defined($options{request}->{cert_pkcs12})) {
push @{$options{request}->{ssl_opt}}, 'SSL_use_cert => 1';
push @{$options{request}->{ssl_opt}}, 'SSL_cert_file => "' . $options{request}->{cert_file} . '"';
push @{$options{request}->{ssl_opt}}, 'SSL_key_file => "' . $options{request}->{key_file} . '"'
if (defined($options{request}->{key_file}));
push @{$options{request}->{ssl_opt}}, 'SSL_ca_file => "' . $options{request}->{cacert_file} . '"'
if (defined($options{request}->{cacert_file}));
}
if ($options{request}->{insecure}) {
push @{$options{request}->{ssl_opt}}, 'SSL_verify_mode => SSL_VERIFY_NONE';
}
my $append = '';
foreach (@{$options{request}->{ssl_opt}}) {
if ($_ ne '') {
$self->{ssl_context} .= $append . $_;
$append = ', ';
}
}
}
sub set_proxy {
my ($self, %options) = @_;
if (defined($options{request}->{proxypac}) && $options{request}->{proxypac} ne '') {
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'HTTP::ProxyPAC',
error_msg => "Cannot load module 'HTTP::ProxyPAC'."
);
my ($pac, $pac_uri);
eval {
if ($options{request}->{proxypac} =~ /^(http|https):\/\//) {
$pac_uri = URI->new($options{request}->{proxypac});
$pac = HTTP::ProxyPAC->new($pac_uri);
} else {
$pac = HTTP::ProxyPAC->new($options{request}->{proxypac});
}
};
if ($@) {
$self->{output}->add_option_msg(short_msg => 'issue to load proxypac: ' . $@);
$self->{output}->option_exit();
}
my $res = $pac->find_proxy($options{url});
if (defined($res->direct) && $res->direct != 1) {
my $proxy_uri = URI->new($res->proxy);
$proxy_uri->userinfo($pac_uri->userinfo) if (defined($pac_uri->userinfo));
$self->{ua}->proxy(['http', 'https'], $proxy_uri->as_string);
}
}
if (defined($options{request}->{proxyurl}) && $options{request}->{proxyurl} ne '') {
my $proxyurl = $options{request}->{proxyurl};
if ($options{request}->{proto} eq "https" ||
(defined($options{request}->{full_url}) && $options{request}->{full_url} =~ /^https/)) {
$proxyurl = 'connect://' . $2 if ($proxyurl =~ /^(http|https):\/\/(.*)/);
}
$self->{ua}->proxy(['http', 'https'], $proxyurl);
}
}
sub request {
my ($self, %options) = @_;
my %user_agent_params = (keep_alive => 1);
if (defined($options{request}->{certinfo}) && $options{request}->{certinfo} == 1) {
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'LWP::ConnCache',
error_msg => "Cannot load module 'LWP::ConnCache'."
);
$self->{cache} = LWP::ConnCache->new();
$self->{cache}->total_capacity(1);
%user_agent_params = (conn_cache => $self->{cache});
}
my $request_options = $options{request};
if (!defined($self->{ua})) {
my $timeout;
$timeout = $1 if (defined($request_options->{timeout}) && $request_options->{timeout} =~ /(\d+)/);
$self->{ua} = centreon::plugins::backend::http::useragent->new(
%user_agent_params,
protocols_allowed => ['http', 'https'],
timeout => $timeout,
credentials => $request_options->{credentials},
username => $request_options->{username},
password => $request_options->{password}
);
if (defined($request_options->{cookies_file})) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'HTTP::Cookies',
error_msg => "Cannot load module 'HTTP::Cookies'."
);
$self->{ua}->cookie_jar(
HTTP::Cookies->new(
file => $request_options->{cookies_file},
autosave => 1
)
);
}
}
if ($self->{output}->is_debug() && $self->{debug_handlers} == 0) {
$self->{debug_handlers} = 1;
$self->{ua}->add_handler('request_send', sub {
my ($response, $ua, $handler) = @_;
$self->{output}->output_add(long_msg => '======> request send', debug => 1);
$self->{output}->output_add(long_msg => $response->as_string, debug => 1);
return ;
});
$self->{ua}->add_handler("response_done", sub {
my ($response, $ua, $handler) = @_;
$self->{output}->output_add(long_msg => '======> response done', debug => 1);
$self->{output}->output_add(long_msg => $response->as_string, debug => 1);
return ;
});
}
if (defined($request_options->{no_follow})) {
$self->{ua}->requests_redirectable(undef);
} else {
$self->{ua}->requests_redirectable([ 'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'PATCH' ]);
}
if (defined($request_options->{http_peer_addr})) {
push @LWP::Protocol::http::EXTRA_SOCK_OPTS, PeerAddr => $request_options->{http_peer_addr};
}
my ($req, $url);
if (defined($request_options->{full_url})) {
$url = $request_options->{full_url};
} elsif (defined($request_options->{port}) && $request_options->{port} =~ /^[0-9]+$/) {
$url = $request_options->{proto}. '://' . $request_options->{hostname} . ':' . $request_options->{port} . $request_options->{url_path};
} else {
$url = $request_options->{proto}. '://' . $request_options->{hostname} . $request_options->{url_path};
}
my $uri = URI->new($url);
if (defined($request_options->{get_params})) {
$uri->query_form($request_options->{get_params});
}
$req = HTTP::Request->new($request_options->{method}, $uri);
my $content_type_forced = 0;
foreach my $key (keys %{$request_options->{headers}}) {
$req->header($key => $request_options->{headers}->{$key});
if ($key =~ /content-type/i) {
$content_type_forced = 1;
}
}
if ($content_type_forced == 1) {
$req->content($request_options->{query_form_post});
} elsif (defined($options{request}->{post_params})) {
my $uri_post = URI->new();
$uri_post->query_form($request_options->{post_params});
$req->content_type('application/x-www-form-urlencoded');
$req->content($uri_post->query);
}
if (defined($request_options->{form})) {
$self->{output}->add_option_msg(short_msg => 'unsupported form param');
$self->{output}->option_exit();
}
if (defined($request_options->{credentials}) && defined($request_options->{ntlmv2})) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Authen::NTLM',
error_msg => "Cannot load module 'Authen::NTLM'."
);
Authen::NTLM::ntlmv2(1);
}
if (defined($request_options->{credentials}) && (defined($request_options->{basic}) || defined($request_options->{digest}))) {
$req->authorization_basic($request_options->{username}, $request_options->{password});
}
$self->set_proxy(request => $request_options, url => $url);
if (defined($request_options->{cert_pkcs12}) && $request_options->{cert_file} ne '' && $request_options->{cert_pwd} ne '') {
eval 'use Net::SSL'; die $@ if $@;
$ENV{HTTPS_PKCS12_FILE} = $request_options->{cert_file};
$ENV{HTTPS_PKCS12_PASSWORD} = $request_options->{cert_pwd};
}
if (defined($self->{ssl_context}) && $self->{ssl_context} ne '') {
my $context = new IO::Socket::SSL::SSL_Context(eval $self->{ssl_context});
IO::Socket::SSL::set_default_context($context);
}
$self->{response} = $self->{ua}->request($req);
$self->{response_code} = $self->{response}->code();
$self->{response_message} = $self->{response}->message();
$self->{headers} = $self->{response}->headers();
if ($self->{response_code} == 500) {
my $client_warning = $self->get_header(name => 'Client-Warning');
if (defined($client_warning) && $client_warning eq 'Internal response' && $self->{response_message} =~ /connection timed out/mi) {
$self->{response_code} = 450;
$self->{response_message} = 'Timeout reached';
}
}
# Check response
my $status = 'ok';
if (defined($request_options->{critical_status}) && $request_options->{critical_status} ne '' &&
$self->{output}->test_eval(test => $request_options->{critical_status}, values => { code => $self->{response_code} })) {
$status = 'critical';
} elsif (defined($request_options->{warning_status}) && $request_options->{warning_status} ne '' &&
$self->{output}->test_eval(test => $request_options->{warning_status}, values => { code => $self->{response_code} })) {
$status = 'warning';
} elsif (defined($request_options->{unknown_status}) && $request_options->{unknown_status} ne '' &&
$self->{output}->test_eval(test => $request_options->{unknown_status}, values => { code => $self->{response_code} })) {
$status = 'unknown';
}
if (!$request_options->{silently_fail} && !$self->{output}->is_status(value => $status, compare => 'ok', litteral => 1)) {
my $short_msg = $self->{response}->status_line;
if ($short_msg =~ /^401/) {
$short_msg .= ' (' . $1 . ' authentication expected)' if (defined($self->{response}->www_authenticate) &&
$self->{response}->www_authenticate =~ /(\S+)/);
}
$self->{output}->output_add(
severity => $status,
short_msg => $short_msg
);
$self->{output}->display();
$self->{output}->exit();
}
return $self->{response}->content;
}
sub get_headers {
my ($self, %options) = @_;
my $headers = '';
foreach ($options{response}->header_field_names()) {
my $value = $options{response}->header($_);
$headers .= "$_: " . (defined($value) ? $value : '') . "\n";
}
return $headers;
}
sub get_first_header {
my ($self, %options) = @_;
my @redirects = $self->{response}->redirects();
if (!defined($options{name})) {
return $self->get_headers(response => defined($redirects[0]) ? $redirects[0] : $self->{response});
}
return
defined($redirects[0]) ?
$redirects[0]->headers()->header($options{name}) :
$self->{headers}->header($options{name})
;
}
sub get_header {
my ($self, %options) = @_;
if (!defined($options{name})) {
return $self->get_headers(response => $self->{response});
}
return $self->{headers}->header($options{name});
}
sub get_code {
my ($self, %options) = @_;
return $self->{response_code};
}
sub get_message {
my ($self, %options) = @_;
return $self->{response_message};
}
sub get_certificate {
my ($self, %options) = @_;
my ($con) = $self->{cache}->get_connections('https');
return ('socket', $con);
}
sub get_times {
my ($self, %options) = @_;
return undef;
}
1;
=head1 NAME
HTTP LWP backend layer.
=head1 SYNOPSIS
HTTP LWP backend layer.
=head1 BACKEND LWP OPTIONS
=over 8
=item B<--ssl-opt>
Set SSL Options (--ssl-opt="SSL_version => TLSv1" --ssl-opt="SSL_verify_mode => SSL_VERIFY_NONE").
=item B<--ssl>
Set SSL version (--ssl=TLSv1).
=back
=head1 DESCRIPTION
B<http>.
=cut
CENTREON_PLUGINS_BACKEND_HTTP_LWP
$fatpacked{"centreon/plugins/backend/http/useragent.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_BACKEND_HTTP_USERAGENT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::backend::http::useragent;
use strict;
use warnings;
use base 'LWP::UserAgent';
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self = LWP::UserAgent::new(@_);
$self->agent("centreon::plugins::backend::http::useragent");
$self->{credentials} = $options{credentials} if defined($options{credentials});
$self->{username} = $options{username} if defined($options{username});
$self->{password} = $options{password} if defined($options{password});
return $self;
}
sub get_basic_credentials {
my($self, $realm, $uri, $proxy) = @_;
return if $proxy;
return $self->{username}, $self->{password} if $self->{credentials} and wantarray;
return $self->{username} . ':' . $self->{password} if $self->{credentials};
return undef;
}
1;
CENTREON_PLUGINS_BACKEND_HTTP_USERAGENT
$fatpacked{"centreon/plugins/curllogger.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_CURLLOGGER';
#
# Copyright 2025 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 centreon::plugins::curllogger;
use strict;
use warnings;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{log_as_curl} = [];
$self->{is_log_as_curl} = 0;
# As this is only used for debugging purposes, we disable it if the ShellQuote
# module is missing
eval "use String::ShellQuote";
if ($@) {
$self->{is_log_as_curl} = -1;
}
$self;
}
sub init {
my ($self, %options) = @_;
$self->{log_as_curl} = [];
return if $self->{is_log_as_curl} == -1;
$self->{is_log_as_curl} = $options{enabled} || 0;
}
sub is_enabled {
my ($self) = @_;
return $self->{is_log_as_curl} == 1;
}
sub log {
my ($self, @params) = @_;
return unless $self->{is_log_as_curl} == 1 && @params;
push @{$self->{log_as_curl}}, shell_quote(@params);
}
sub get_log {
my ($self) = @_;
return "curl ".join ' ', @{$self->{log_as_curl}};
}
# Conversion of some parameters manually passed to the set_extra_curl_opt function
# into their command-line equivalents. Only the parameters used in the plugin code
# are handled. If new parameters are added, this hash must be updated.
# The hash contains curl parameters CURLOPT_*. Its keys map to either a hash when
# multiple values are handled, or directly to an array when only one response is
# supported. The placeholder <value> is replaced by the provided value.
# Eg: "CURLOPT_POSTREDIR => CURL_REDIR_POST_ALL" will produce: --post301 --post302 --post303
# "CURLOPT_SSL_VERIFYPEER => 0" will produce: --insecure
# "CURLOPT_AWS_SIGV4 => 'osc'" will produce: --aws-sigv4 osc
our %curlopt_to_parameter = (
'CURLOPT_POSTREDIR' => { 'CURL_REDIR_POST_ALL' => [ '--post301', '--post302', '--post303', ],
'CURL_REDIR_POST_301' => [ '--post301' ],
'CURL_REDIR_POST_302' => [ '--post302' ],
'CURL_REDIR_POST_303' => [ '--post303' ],
},
'CURLOPT_SSLVERSION' => { 'CURL_SSLVERSION_TLSv1_0' => [ '--tlsv1.0' ],
'CURL_SSLVERSION_TLSv1_1' => [ '--tlsv1.1' ],
'CURL_SSLVERSION_TLSv1_2' => [ '--tlsv1.2' ],
'CURL_SSLVERSION_TLSv1_3' => [ '--tlsv1.3' ],
},
'CURLOPT_SSL_VERIFYPEER' => { '0' => [ '--insecure' ] },
'CURLOPT_SSL_VERIFYHOST' => { '0' => [ '--insecure' ] },
'CURLOPT_AWS_SIGV4' => [ '--aws-sigv4', '<value>' ],
);
sub convert_curlopt_to_cups_parameter {
my ($self, %options) = @_;
my $key = $options{key};
return unless exists $curlopt_to_parameter{$key};
# we want an array of parameters
my $parameters = ref $options{parameter} eq 'ARRAY' ? $options{parameter} : [ $options{parameter} ] ;
my @cups_parameters ;
if (ref $curlopt_to_parameter{$key} eq 'ARRAY') {
@cups_parameters = map { s/<value>/$parameters->[0]/; $_ } @{$curlopt_to_parameter{$key}};
} else {
foreach my $parameter (@$parameters) {
if (exists $curlopt_to_parameter{$key}->{$parameter}) {
push @cups_parameters, @{$curlopt_to_parameter{$key}->{$parameter}};
} elsif ($parameter =~ /^-/) {
push @cups_parameters, $parameter;
}
}
}
$self->log($_) for @cups_parameters;
}
1;
CENTREON_PLUGINS_CURLLOGGER
$fatpacked{"centreon/plugins/dbi.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_DBI';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::dbi;
use strict;
use warnings;
use DBI;
use Digest::MD5 qw(md5_hex);
use POSIX qw(:signal_h);
my %handlers = (ALRM => {});
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
# $options{options} = options object
# $options{output} = output object
# $options{exit_value} = integer
# $options{noptions} = integer
if (!defined($options{output})) {
print "Class DBI: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class DBI: Need to specify 'options' argument.");
$options{output}->option_exit();
}
if (!defined($options{noptions})) {
$options{options}->add_options(arguments => {
'datasource:s@' => { name => 'data_source' },
'username:s@' => { name => 'username' },
'password:s@' => { name => 'password' },
'connect-options:s@' => { name => 'connect_options' },
'connect-query:s@' => { name => 'connect_query' },
'sql-errors-exit:s' => { name => 'sql_errors_exit', default => 'unknown' },
'timeout:s' => { name => 'timeout' },
'exec-timeout:s' => { name => 'exec_timeout' }
});
}
$options{options}->add_help(package => __PACKAGE__, sections => 'DBI OPTIONS', once => 1);
$self->{output} = $options{output};
$self->{sqlmode_name} = $options{sqlmode_name};
$self->{instance} = undef;
$self->{statement_handle} = undef;
$self->{version} = undef;
$self->{data_source} = undef;
$self->{username} = undef;
$self->{password} = undef;
$self->{connect_options} = undef;
$self->{connect_options_hash} = {};
# Sometimes, we need to set ENV
$self->{env} = undef;
return $self;
}
sub prepare_destroy {
my ($self) = @_;
%handlers = ();
}
sub set_signal_handlers {
my $self = shift;
$SIG{ALRM} = \&class_handle_ALRM;
$handlers{ALRM}->{$self} = sub { $self->handle_ALRM() };
}
sub class_handle_ALRM {
foreach (keys %{$handlers{ALRM}}) {
&{$handlers{ALRM}->{$_}}();
}
}
sub handle_ALRM {
my $self = shift;
$self->prepare_destroy();
$self->disconnect();
$self->{output}->output_add(
severity => $self->{sql_errors_exit},
short_msg => 'Timeout'
);
$self->{output}->display();
$self->{output}->exit();
}
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->{sqlmode_name}) {
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->{data_source} = (defined($self->{option_results}->{data_source})) ? shift(@{$self->{option_results}->{data_source}}) : undef;
$self->{username} = undef;
if (defined($self->{option_results}->{username})) {
$self->{username} = ref($self->{option_results}->{username}) eq 'ARRAY' ? shift(@{$self->{option_results}->{username}}) : $self->{option_results}->{username};
}
$self->{password} = undef;
if (defined($self->{option_results}->{password})) {
$self->{password} = ref($self->{option_results}->{password}) eq 'ARRAY' ? shift(@{$self->{option_results}->{password}}) : $self->{option_results}->{password};
}
$self->{connect_options} = (defined($self->{option_results}->{connect_options})) ? shift(@{$self->{option_results}->{connect_options}}) : undef;
$self->{connect_query} = (defined($self->{option_results}->{connect_query})) ? shift(@{$self->{option_results}->{connect_query}}) : undef;
$self->{env} = (defined($self->{option_results}->{env})) ? shift(@{$self->{option_results}->{env}}) : undef;
$self->{sql_errors_exit} = $self->{option_results}->{sql_errors_exit};
$self->{timeout} = 10;
if (defined($self->{option_results}->{timeout}) && $self->{option_results}->{timeout} =~ /^\d+$/ &&
$self->{option_results}->{timeout} > 0) {
$self->{timeout} = $self->{option_results}->{timeout};
}
if (defined($self->{option_results}->{exec_timeout}) && $self->{option_results}->{exec_timeout} =~ /^\d+$/ &&
$self->{option_results}->{exec_timeout} > 0) {
$self->{exec_timeout} = $self->{option_results}->{exec_timeout};
}
if (!defined($self->{data_source}) || $self->{data_source} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify data_source arguments.');
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
if (defined($self->{connect_options}) && $self->{connect_options} ne '') {
foreach my $entry (split /,/, $self->{connect_options}) {
if ($entry !~ /^\s*([^=]+)=([^=]+)\s*$/) {
$self->{output}->add_option_msg(short_msg => "Wrong format for --connect-options '" . $entry . "'.");
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
$self->{connect_options_hash}->{$1} = $2;
}
}
if (scalar(@{$self->{option_results}->{data_source}}) == 0) {
return 0;
}
return 1;
}
sub quote {
my $self = shift;
if (defined($self->{instance})) {
return $self->{instance}->quote($_[0]);
}
return undef;
}
sub is_version_minimum {
my ($self, %options) = @_;
# $options{version} = string version to check
my @version_src = split /\./, $self->{version};
my @versions = split /\./, $options{version};
for (my $i = 0; $i < scalar(@versions); $i++) {
return 1 if ($versions[$i] eq 'x');
return 1 if (!defined($version_src[$i]));
$version_src[$i] =~ /^([0-9]*)/;
next if ($versions[$i] == int($1));
return 0 if ($versions[$i] > int($1));
return 1 if ($versions[$i] < int($1));
}
return 1;
}
sub set_version {
my ($self) = @_;
$self->{version} = $self->{instance}->get_info(18); # SQL_DBMS_VER
}
sub disconnect {
my ($self) = @_;
if (defined($self->{instance})) {
$self->{statement_handle} = undef;
$self->{instance}->disconnect();
$self->{instance} = undef;
}
}
sub connect {
my ($self, %options) = @_;
my $dontquit = (defined($options{dontquit}) && $options{dontquit} == 1) ? 1 : 0;
return if (defined($self->{instance}));
# Set ENV
if (defined($self->{env})) {
foreach (keys %{$self->{env}}) {
$ENV{$_} = $self->{env}->{$_};
}
}
$self->set_signal_handlers();
my $connect_error;
eval {
alarm($self->{timeout}) if (defined($self->{timeout}));
$self->{instance} = DBI->connect(
"DBI:" . $self->{data_source},
$self->{username},
$self->{password},
{ RaiseError => 0, PrintError => 0, AutoCommit => 1, %{$self->{connect_options_hash}} }
);
alarm(0) if (defined($self->{timeout}));
};
if ($@) {
$connect_error = $@;
}
$self->prepare_destroy();
if (!defined($self->{instance})) {
my $err_msg = sprintf(
'Cannot connect: %s',
defined($DBI::errstr) ? $DBI::errstr :
(defined($connect_error) ? $connect_error : '(no error string)')
);
if ($dontquit == 0) {
$self->{output}->add_option_msg(short_msg => $err_msg);
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
return (-1, $err_msg);
}
if (defined($self->{connect_query}) && $self->{connect_query} ne '') {
$self->query(query => $self->{connect_query});
}
$self->set_version();
return 0;
}
sub get_id {
my ($self, %options) = @_;
return $self->{data_source};
}
sub get_unique_id4save {
my ($self, %options) = @_;
return md5_hex($self->{data_source});
}
sub fetchall_arrayref {
my ($self, %options) = @_;
return $self->{statement_handle}->fetchall_arrayref();
}
sub fetchrow_array {
my ($self, %options) = @_;
return $self->{statement_handle}->fetchrow_array();
}
sub fetchrow_hashref {
my ($self, %options) = @_;
return $self->{statement_handle}->fetchrow_hashref();
}
sub query {
my ($self, %options) = @_;
my $continue_error = defined($options{continue_error}) && $options{continue_error} == 1 ? 1 : 0;
$self->{statement_handle} = $self->{instance}->prepare($options{query});
if (!defined($self->{statement_handle})) {
return 1 if ($continue_error == 1);
$self->{output}->add_option_msg(short_msg => 'Cannot execute query: ' . $self->{instance}->errstr);
$self->disconnect();
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
my $rv;
if (defined($self->{exec_timeout})) {
my $mask = POSIX::SigSet->new(SIGALRM);
my $action = POSIX::SigAction->new(
sub { $self->handle_ALRM() },
$mask,
);
my $oldaction = POSIX::SigAction->new();
sigaction(SIGALRM, $action, $oldaction);
eval {
eval {
alarm($self->{exec_timeout});
$rv = $self->{statement_handle}->execute();
};
alarm(0);
};
sigaction(SIGALRM, $oldaction);
} else {
$rv = $self->{statement_handle}->execute();
}
if (!$rv) {
return 1 if ($continue_error == 1);
$self->{output}->add_option_msg(short_msg => 'Cannot execute query: ' . $self->{statement_handle}->errstr);
$self->disconnect();
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
return 0;
}
1;
=head1 NAME
DBI global
=head1 SYNOPSIS
DBI class
=head1 DBI OPTIONS
=over 8
=item B<--datasource>
Database server information, mandatory if the server's address and port are not
defined in the corresponding options. The syntax depends on the database type.
=item B<--username>
User name used to connect to the database.
=item B<--password>
Password for the defined user name.
=item B<--connect-options>
Add connection options for the DBI connect method.
Format: name=value,name2=value2,...
=item B<--connect-query>
Execute a query just after the connection.
=item B<--sql-errors-exit>
Expected status in case of DB error or timeout.
Possible values are warning, critical and unknown (default).
=item B<--timeout>
Timeout in seconds for connection.
=item B<--exec-timeout>
Timeout in seconds for query execution
=back
=head1 DESCRIPTION
B<DBI>.
=cut
CENTREON_PLUGINS_DBI
$fatpacked{"centreon/plugins/http.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_HTTP';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::http;
use strict;
use warnings;
use centreon::plugins::curllogger;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{noptions}) || $options{noptions} != 1) {
$options{options}->add_options(arguments => {
'http-peer-addr:s' => { name => 'http_peer_addr' },
'proxyurl:s' => { name => 'proxyurl' },
'proxypac:s' => { name => 'proxypac' },
'insecure' => { name => 'insecure' },
'http-backend:s' => { name => 'http_backend' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'HTTP GLOBAL OPTIONS');
}
my $curllogger = centreon::plugins::curllogger->new();
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::http::lwp',
error_msg => "Cannot load module 'centreon::plugins::backend::http::lwp'."
);
$self->{backend_lwp} = centreon::plugins::backend::http::lwp->new(%options);
centreon::plugins::misc::mymodule_load(
output => $options{output},
module => 'centreon::plugins::backend::http::curl',
error_msg => "Cannot load module 'centreon::plugins::backend::http::curl'."
);
$self->{backend_curl} = centreon::plugins::backend::http::curl->new(%options, curl_logger => $curllogger);
$self->{default_backend} = defined($options{default_backend}) && $options{default_backend} ne '' ?
$options{default_backend} : 'lwp';
$self->{output} = $options{output};
$self->{options} = {
proto => 'http',
url_path => '/',
timeout => 5,
method => 'GET',
unknown_status => '%{http_code} < 200 or %{http_code} >= 300',
warning_status => undef,
critical_status => undef,
};
$self->{add_headers} = {};
return $self;
}
sub set_options {
my ($self, %options) = @_;
$self->{options} = { %{$self->{options}} };
foreach (keys %options) {
$self->{options}->{$_} = $options{$_} if (defined($options{$_}));
}
}
sub add_header {
my ($self, %options) = @_;
$self->{add_headers}->{$options{key}} = $options{value};
}
sub remove_header {
my ($self, %options) = @_;
delete $self->{add_headers}->{$options{key}} if (defined($self->{add_headers}->{$options{key}}));
}
sub check_options {
my ($self, %options) = @_;
$options{request}->{http_backend} = $self->{default_backend}
if (!defined($options{request}->{http_backend}) || $options{request}->{http_backend} eq '');
$self->{http_backend} = $options{request}->{http_backend};
if ($self->{http_backend} !~ /^\s*lwp|curl\s*$/i) {
$self->{output}->add_option_msg(short_msg => "Unsupported http backend specified '" . $self->{http_backend} . "'.");
$self->{output}->option_exit();
}
if (defined($options{request}->{$self->{http_backend} . '_backend_options'})) {
foreach (keys %{$options{request}->{$self->{http_backend} . '_backend_options'}}) {
$options{request}->{$_} = $options{request}->{$self->{http_backend} . '_backend_options'}->{$_};
}
}
if (($options{request}->{proto} ne 'http') && ($options{request}->{proto} ne 'https')) {
$self->{output}->add_option_msg(short_msg => "Unsupported protocol specified: '(" . $options{request}->{proto} . ")'. . Use either https or http.");
$self->{output}->option_exit();
}
if (!defined($options{request}->{hostname})) {
$self->{output}->add_option_msg(short_msg => "Please set the hostname option");
$self->{output}->option_exit();
}
if ((defined($options{request}->{credentials})) && (!defined($options{request}->{username}) || !defined($options{request}->{password}))) {
$self->{output}->add_option_msg(short_msg => "You need to set --username= and --password= options when --credentials is used");
$self->{output}->option_exit();
}
if ((defined($options{request}->{cert_pkcs12})) && (!defined($options{request}->{cert_file}) && !defined($options{request}->{cert_pwd}))) {
$self->{output}->add_option_msg(short_msg => "You need to set --cert-file= and --cert-pwd= options when --pkcs12 is used");
$self->{output}->option_exit();
}
$options{request}->{port_force} = $self->get_port();
$options{request}->{headers} = {};
if (defined($options{request}->{header})) {
foreach (@{$options{request}->{header}}) {
if (/^(:.+?|.+?):(.*)/) {
$options{request}->{headers}->{$1} = $2;
}
}
}
foreach (keys %{$self->{add_headers}}) {
$options{request}->{headers}->{$_} = $self->{add_headers}->{$_};
}
foreach my $method (('get', 'post')) {
if (defined($options{request}->{$method . '_param'})) {
$options{request}->{$method . '_params'} = {};
foreach (@{$options{request}->{$method . '_param'}}) {
if (/^([^=]+)={0,1}(.*)$/s) {
my $key = $1;
my $value = defined($2) ? $2 : 1;
if (defined($options{request}->{$method . '_params'}->{$key})) {
if (ref($options{request}->{$method . '_params'}->{$key}) ne 'ARRAY') {
$options{request}->{$method . '_params'}->{$key} = [ $options{request}->{$method . '_params'}->{$key} ];
}
push @{$options{request}->{$method . '_params'}->{$key}}, $value;
} else {
$options{request}->{$method . '_params'}->{$key} = $value;
}
}
}
}
}
$self->{'backend_' . $self->{http_backend}}->check_options(%options);
}
sub get_port {
my ($self, %options) = @_;
my $port = '';
if (defined($self->{options}->{port}) && $self->{options}->{port} ne '') {
$port = $self->{options}->{port};
} else {
$port = 80 if ($self->{options}->{proto} eq 'http');
$port = 443 if ($self->{options}->{proto} eq 'https');
}
return $port;
}
sub get_port_request {
my ($self, %options) = @_;
my $port = '';
if (defined($self->{options}->{port}) && $self->{options}->{port} ne '') {
$port = $self->{options}->{port};
}
return $port;
}
sub request {
my ($self, %options) = @_;
my $request_options = { %{$self->{options}} };
foreach (keys %options) {
$request_options->{$_} = $options{$_} if (defined($options{$_}));
}
$self->check_options(request => $request_options);
return $self->{'backend_' . $self->{http_backend}}->request(request => $request_options);
}
sub get_first_header {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_first_header(%options);
}
sub get_header {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_header(%options);
}
sub get_code {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_code();
}
sub get_message {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_message();
}
sub get_certificate {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_certificate();
}
sub get_times {
my ($self, %options) = @_;
return $self->{'backend_' . $self->{http_backend}}->get_times();
}
1;
=head1 NAME
HTTP abstraction layer.
=head1 SYNOPSIS
HTTP abstraction layer for lwp and curl backends
=head1 HTTP GLOBAL OPTIONS
=over 8
=item B<--http-peer-addr>
Set the address you want to connect to. Useful if hostname is only a vhost, to avoid IP resolution.
=item B<--proxyurl>
Proxy URL. Example: http://my.proxy:3128
=item B<--proxypac>
Proxy PAC file (can be a URL or a local file).
=item B<--insecure>
Accept insecure SSL connections.
=item B<--http-backend>
Perl library to use for HTTP transactions.
Possible values are: lwp (default) and curl.
=back
=head1 DESCRIPTION
B<http>.
=cut
CENTREON_PLUGINS_HTTP
$fatpacked{"centreon/plugins/misc.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MISC';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::misc;
use strict;
use warnings;
use utf8;
use JSON::XS;
sub execute {
my (%options) = @_;
if ($^O eq 'MSWin32') {
return windows_execute(%options, timeout => $options{options}->{timeout});
} else {
return unix_execute(%options);
}
}
sub windows_execute {
my (%options) = @_;
my $result;
my ($stdout, $pid, $ended) = ('');
my ($exit_code, $cmd);
$cmd = $options{command_path} . '/' if (defined($options{command_path}));
$cmd .= $options{command} . ' ' if (defined($options{command}));
$cmd .= $options{command_options} if (defined($options{command_options}));
centreon::plugins::misc::mymodule_load(
output => $options{output}, module => 'Win32::Job',
error_msg => "Cannot load module 'Win32::Job'."
);
centreon::plugins::misc::mymodule_load(
output => $options{output}, module => 'Time::HiRes',
error_msg => "Cannot load module 'Time::HiRes'."
);
$| = 1;
pipe FROM_CHILD, TO_PARENT or do {
$options{output}->add_option_msg(short_msg => "Internal error: can't create pipe from child to parent: $!");
$options{output}->option_exit();
};
my $job = Win32::Job->new;
my $stderr = 'NUL';
$stderr = \*TO_PARENT if ($options{output}->is_debug());
if (!($pid = $job->spawn(undef, $cmd,
{ stdin => 'NUL',
stdout => \*TO_PARENT,
stderr => $stderr }))) {
$options{output}->add_option_msg(short_msg => "Internal error: execution issue: $^E");
$options{output}->option_exit();
}
close TO_PARENT;
my $ein = '';
vec($ein, fileno(FROM_CHILD), 1) = 1;
$job->watch(
sub {
my ($buffer);
my $time = $options{timeout};
my $last_time = Time::HiRes::time();
$ended = 0;
while (select($ein, undef, undef, $options{timeout})) {
if (sysread(FROM_CHILD, $buffer, 16384)) {
$buffer =~ s/\r//g;
$stdout .= $buffer;
} else {
$ended = 1;
last;
}
$options{timeout} -= Time::HiRes::time() - $last_time;
last if ($options{timeout} <= 0);
$last_time = Time::HiRes::time();
}
return 1 if ($ended == 0);
return 0;
},
0.1
);
$result = $job->status;
close FROM_CHILD;
if ($ended == 0) {
$options{output}->add_option_msg(short_msg => 'Command too long to execute (timeout)...');
$options{output}->option_exit();
}
chomp $stdout;
if (defined($options{no_quit}) && $options{no_quit} == 1) {
return ($stdout, $result->{$pid}->{exitcode});
}
if ($result->{$pid}->{exitcode} != 0) {
$stdout =~ s/\n/ - /g;
$options{output}->add_option_msg(short_msg => "Command error: $stdout");
$options{output}->option_exit();
}
return ($stdout, $result->{$pid}->{exitcode});
}
sub unix_execute {
my (%options) = @_;
my $cmd = '';
my $args = [];
my ($lerror, $stdout, $exit_code);
my $redirect_stderr = 1;
$redirect_stderr = $options{redirect_stderr} if (defined($options{redirect_stderr}));
my $wait_exit = 1;
$wait_exit = $options{wait_exit} if (defined($options{wait_exit}));
# Build command line
# Can choose which command is done remotely (can filter and use local file)
if (defined($options{options}->{remote}) &&
($options{options}->{remote} eq '' || !defined($options{label}) || $options{label} =~ /$options{options}->{remote}/)) {
my $sub_cmd;
$cmd = $options{options}->{ssh_path} . '/' if (defined($options{options}->{ssh_path}));
$cmd .= $options{options}->{ssh_command} if (defined($options{options}->{ssh_command}));
foreach (@{$options{options}->{ssh_option}}) {
if (/^(.*?)(?:=(.*))?$/) {
push @$args, $1 if (defined($1));
push @$args, $2 if (defined($2));
}
}
if (defined($options{options}->{ssh_address}) && $options{options}->{ssh_address} ne '') {
push @$args, $options{options}->{ssh_address};
} else {
push @$args, $options{options}->{hostname};
}
if (defined($options{options}->{ssh_option_eol})) {
foreach (@{$options{options}->{ssh_option_eol}}) {
if (/^(.*?)(?:=(.*))?$/) {
push @$args, $1 if (defined($1));
push @$args, $2 if (defined($2));
}
}
}
$sub_cmd = 'sudo ' if (defined($options{sudo}));
$sub_cmd .= $options{command_path} . '/' if (defined($options{command_path}));
$sub_cmd .= $options{command} . ' ' if (defined($options{command}));
$sub_cmd .= $options{command_options} if (defined($options{command_options}));
# On some equipment. Cannot get a pseudo terminal
if (defined($options{ssh_pipe}) && $options{ssh_pipe} == 1) {
$cmd = "echo '" . $sub_cmd . "' | " . $cmd . ' ' . join(' ', @$args);
($lerror, $stdout, $exit_code) = backtick(
command => $cmd,
timeout => $options{options}->{timeout},
wait_exit => $wait_exit,
redirect_stderr => $redirect_stderr
);
} else {
($lerror, $stdout, $exit_code) = backtick(
command => $cmd,
arguments => [@$args, $sub_cmd],
timeout => $options{options}->{timeout},
wait_exit => $wait_exit,
redirect_stderr => $redirect_stderr
);
}
} else {
$cmd = 'sudo ' if (defined($options{sudo}));
$cmd .= $options{command_path} . '/' if (defined($options{command_path}));
$cmd .= $options{command} if (defined($options{command}));
$cmd .= ' ' . $options{command_options} if (defined($options{command_options}));
if (defined($options{no_shell_interpretation}) and $options{no_shell_interpretation} ne '') {
my @args = split(' ',$cmd);
($lerror, $stdout, $exit_code) = backtick(
command => $args[0],
arguments => [@args[1.. $#args]],
timeout => $options{options}->{timeout},
wait_exit => $wait_exit,
redirect_stderr => $redirect_stderr
);
}
else {
($lerror, $stdout, $exit_code) = backtick(
command => $cmd,
timeout => $options{options}->{timeout},
wait_exit => $wait_exit,
redirect_stderr => $redirect_stderr
);
}
}
if (defined($options{options}->{show_output}) &&
($options{options}->{show_output} eq '' || (defined($options{label}) && $options{label} eq $options{options}->{show_output}))) {
print $stdout;
exit $exit_code;
}
$stdout =~ s/\r//g;
if ($lerror <= -1000) {
$options{output}->add_option_msg(short_msg => $stdout);
$options{output}->option_exit();
}
if (defined($options{no_quit}) && $options{no_quit} == 1) {
return ($stdout, $exit_code);
}
if ($exit_code != 0 && (!defined($options{no_errors}) || !defined($options{no_errors}->{$exit_code}))) {
$stdout =~ s/\n/ - /g;
$options{output}->add_option_msg(short_msg => "Command error: $stdout");
$options{output}->option_exit();
}
return $stdout;
}
sub mymodule_load {
my (%options) = @_;
my $file;
($file = ($options{module} =~ /\.pm$/ ? $options{module} : $options{module} . '.pm')) =~ s{::}{/}g;
eval {
local $SIG{__DIE__} = 'IGNORE';
require $file;
$file =~ s{/}{::}g;
$file =~ s/\.pm$//;
};
if ($@) {
return 1 if (defined($options{no_quit}) && $options{no_quit} == 1);
$options{output}->add_option_msg(long_msg => $@);
$options{output}->add_option_msg(short_msg => $options{error_msg});
$options{output}->option_exit();
}
return wantarray ? (0, $file) : 0;
}
sub backtick {
my %arg = (
command => undef,
arguments => [],
timeout => 30,
wait_exit => 0,
redirect_stderr => 0,
@_,
);
my @output;
my $pid;
my $return_code;
my $sig_do;
if ($arg{wait_exit} == 0) {
$sig_do = 'IGNORE';
$return_code = undef;
} else {
$sig_do = 'DEFAULT';
}
local $SIG{CHLD} = $sig_do;
$SIG{TTOU} = 'IGNORE';
$| = 1;
if (!defined($pid = open( KID, "-|" ))) {
return (-1001, "Cant fork: $!", -1);
}
if ($pid) {
eval {
local $SIG{ALRM} = sub { die "Timeout by signal ALARM\n"; };
alarm( $arg{timeout} );
while (<KID>) {
chomp;
push @output, $_;
}
alarm(0);
};
if ($@) {
if ($pid != -1) {
kill -9, $pid;
}
alarm(0);
return (-1000, 'Command too long to execute (timeout)...', -1);
} else {
if ($arg{wait_exit} == 1) {
# We're waiting the exit code
waitpid($pid, 0);
$return_code = ($? >> 8);
}
close KID;
}
} else {
# child
# set the child process to be a group leader, so that
# kill -9 will kill it and all its descendents
# We have ignore SIGTTOU to let write background processes
setpgrp( 0, 0 );
if ($arg{redirect_stderr} == 1) {
open STDERR, '>&STDOUT';
}
if (scalar(@{$arg{arguments}}) <= 0) {
exec($arg{command});
} else {
exec($arg{command}, @{$arg{arguments}});
}
# Exec is in error. No such command maybe.
exit(127);
}
return (0, join("\n", @output), $return_code);
}
sub is_empty {
my $value = shift;
if (!defined($value) or $value eq '') {
return 1;
}
return 0;
}
sub trim {
my ($value) = $_[0];
# Sometimes there is a null character
$value =~ s/\x00$//;
$value =~ s/^[ \t\n]+//;
$value =~ s/[ \t\n]+$//;
return $value;
}
sub powershell_encoded {
require Encode;
require MIME::Base64;
my $bytes = Encode::encode('utf16LE', $_[0]);
return MIME::Base64::encode_base64($bytes, '');
}
sub powershell_escape {
my ($value) = $_[0];
$value =~ s/`/``/g;
$value =~ s/#/`#/g;
$value =~ s/'/`'/g;
$value =~ s/"/`"/g;
return $value;
}
sub minimal_version {
my ($version_src, $version_dst) = @_;
# No Version. We skip
if (!defined($version_src) || !defined($version_dst) ||
$version_src !~ /^[0-9]+(?:\.[0-9\.]+)*$/ || $version_dst !~ /^[0-9x]+(?:\.[0-9x]+)*$/) {
return 1;
}
my @version_src = split /\./, $version_src;
my @versions = split /\./, $version_dst;
for (my $i = 0; $i < scalar(@versions); $i++) {
return 1 if ($versions[$i] eq 'x');
return 1 if (!defined($version_src[$i]));
$version_src[$i] =~ /^([0-9]*)/;
next if ($versions[$i] == int($1));
return 0 if ($versions[$i] > int($1));
return 1 if ($versions[$i] < int($1));
}
return 1;
}
sub change_seconds {
my %options = @_;
my ($str, $str_append) = ('', '');
my $periods = [
{ unit => 'y', value => 31556926 },
{ unit => 'M', value => 2629743 },
{ unit => 'w', value => 604800 },
{ unit => 'd', value => 86400 },
{ unit => 'h', value => 3600 },
{ unit => 'm', value => 60 },
{ unit => 's', value => 1 },
];
my %values = ('y' => 1, 'M' => 2, 'w' => 3, 'd' => 4, 'h' => 5, 'm' => 6, 's' => 7);
my $sign = '';
if ($options{value} < 0) {
$sign = '-';
$options{value} = abs($options{value});
}
foreach (@$periods) {
next if (defined($options{start}) && $values{$_->{unit}} < $values{$options{start}});
my $count = int($options{value} / $_->{value});
next if ($count == 0);
$str .= $str_append . $count . $_->{unit};
$options{value} = $options{value} % $_->{value};
$str_append = ' ';
}
if ($str eq '') {
$str = $options{value};
$str .= $options{start} if (defined($options{start}));
}
return $sign . $str;
}
sub scale_bytesbit {
my (%options) = @_;
my $base = 1024;
if (defined($options{dst_unit}) && defined($options{src_unit})) {
$options{value} *= 8 if ($options{dst_unit} =~ /b/ && $options{src_unit} =~ /B/);
$options{value} /= 8 if ($options{dst_unit} =~ /B/ && $options{src_unit} =~ /b/);
if ($options{dst_unit} =~ /b/) {
$base = 1000;
}
}
my %expo = ('' => 0, k => 1, m => 2, g => 3, t => 4, p => 5, e => 6);
my ($src_expo, $dst_expo) = (0, 0);
$src_expo = $expo{lc($options{src_quantity})} if (defined($options{src_quantity}) && $options{src_quantity} =~ /[kmgtpe]/i);
if ($options{dst_unit} eq 'auto') {
my @auto = ('', 'k', 'm', 'g', 't', 'p', 'e');
my $i = defined($options{src_quantity}) ? $expo{$options{src_quantity}} : 0;
for (; $i < scalar(@auto); $i++) {
last if ($options{value} < $base);
$options{value} = $options{value} / $base;
}
return ($options{value}, $auto[$i], $options{src_unit});
} elsif (defined($options{dst_quantity}) && ($options{dst_quantity} eq '' || $options{dst_quantity} =~ /[kmgtpe]/i )) {
my $dst_expo = $expo{lc($options{dst_quantity})};
if ($dst_expo - $src_expo > 0) {
$options{value} = $options{value} / ($base ** ($dst_expo - $src_expo));
} elsif ($dst_expo - $src_expo < 0) {
$options{value} = $options{value} * ($base ** (($dst_expo - $src_expo) * -1));
}
}
return $options{value};
}
sub convert_bytes {
my (%options) = @_;
my %expo = (k => 1, m => 2, g => 3, t => 4, p => 5);
my ($value, $unit) = ($options{value}, $options{unit});
if (defined($options{pattern})) {
return undef if ($value !~ /$options{pattern}/);
$value = $1;
$unit = $2;
}
my $base = defined($options{network}) ? 1000 : 1024;
if ($unit =~ /([kmgtp])i?b/i) {
$value = $value * ($base ** $expo{lc($1)});
}
return $value;
}
sub convert_fahrenheit {
my (%options) = @_;
return ($options{value} - 32) / 1.8;
}
sub expand_exponential {
my (%options) = @_;
return $options{value} unless ($options{value} =~ /^(.*)e([-+]?)(.*)$/);
my ($num, $sign, $exp) = ($1, $2, $3);
my $sig = $sign eq '-' ? "." . ($exp - 1 + length $num) : '';
return sprintf("%${sig}f", $options{value});
}
sub alert_triggered {
my (%options) = @_;
my ($rv_warn, $warning) = parse_threshold(threshold => $options{warning});
my ($rv_crit, $critical) = parse_threshold(threshold => $options{critical});
foreach ([$rv_warn, $warning], [$rv_crit, $critical]) {
next if ($_->[0] == 0);
if ($_->[1]->{arobase} == 0 && ($options{value} < $_->[1]->{start} || $options{value} > $_->[1]->{end})) {
return 1;
} elsif ($_->[1]->{arobase} == 1 && ($options{value} >= $_->[1]->{start} && $options{value} <= $_->[1]->{end})) {
return 1;
}
}
return 0;
}
sub parse_threshold {
my (%options) = @_;
my $perf = trim($options{threshold});
my $perf_result = { arobase => 0, infinite_neg => 0, infinite_pos => 0, start => '', end => '' };
my $global_status = 1;
if ($perf =~ /^(\@?)((?:~|(?:\+|-)?\d+(?:[\.,]\d+)?(?:[KMGTPE][bB])?|):)?((?:\+|-)?\d+(?:[\.,]\d+)?(?:[KMGTPE][bB])?)?$/) {
$perf_result->{start} = $2 if (defined($2));
$perf_result->{end} = $3 if (defined($3));
$perf_result->{arobase} = 1 if (defined($1) && $1 eq '@');
$perf_result->{start} =~ s/[\+:]//g;
$perf_result->{end} =~ s/\+//;
if ($perf_result->{start} =~ s/([KMGTPE])([bB])//) {
$perf_result->{start} = scale_bytesbit(
value => $perf_result->{start},
src_unit => $2, dst_unit => $2,
src_quantity => $1, dst_quantity => '',
);
}
if ($perf_result->{end} =~ s/([KMGTPE])([bB])//) {
$perf_result->{end} = scale_bytesbit(
value => $perf_result->{end},
src_unit => $2, dst_unit => $2,
src_quantity => $1, dst_quantity => '',
);
}
if ($perf_result->{end} eq '') {
$perf_result->{end} = 1e500;
$perf_result->{infinite_pos} = 1;
}
$perf_result->{start} = 0 if ($perf_result->{start} eq '');
$perf_result->{start} =~ s/,/\./;
$perf_result->{end} =~ s/,/\./;
if ($perf_result->{start} eq '~') {
$perf_result->{start} = -1e500;
$perf_result->{infinite_neg} = 1;
}
} else {
$global_status = 0;
}
return ($global_status, $perf_result);
}
sub get_threshold_litteral {
my (%options) = @_;
my $perf_output = ($options{arobase} == 1 ? '@' : '') .
(($options{infinite_neg} == 0) ? $options{start} : '~') .
':' .
(($options{infinite_pos} == 0) ? $options{end} : '');
return $perf_output;
}
sub set_timezone {
my (%options) = @_;
return {} if (!defined($options{name}) || $options{name} eq '');
centreon::plugins::misc::mymodule_load(
output => $options{output}, module => 'DateTime::TimeZone',
error_msg => "Cannot load module 'DateTime::TimeZone'."
);
if (DateTime::TimeZone->is_valid_name($options{name})) {
return { time_zone => DateTime::TimeZone->new(name => $options{name}) };
}
# try to manage syntax (:Pacific/Noumea for example)
if ($options{name} =~ /^:(.*)$/ && DateTime::TimeZone->is_valid_name($1)) {
return { time_zone => DateTime::TimeZone->new(name => $1) };
}
return {};
}
sub uniq {
my %seen;
return grep { !$seen{$_}++ } @_;
}
sub eval_ssl_options {
my (%options) = @_;
my $ssl_context = {};
return $ssl_context if (!defined($options{ssl_opt}));
my ($rv) = centreon::plugins::misc::mymodule_load(
output => $options{output}, module => 'Safe',
no_quit => 1
);
centreon::plugins::misc::mymodule_load(
output => $options{output}, module => 'IO::Socket::SSL',
no_quit => 1
);
my $safe;
if ($rv == 0) {
$safe = Safe->new();
$safe->permit_only(':base_core', 'rv2gv', 'padany');
$safe->share('$values');
$safe->share('$assign_var');
$safe->share_from('IO::Socket::SSL', [
'SSL_VERIFY_NONE', 'SSL_VERIFY_PEER', 'SSL_VERIFY_FAIL_IF_NO_PEER_CERT', 'SSL_VERIFY_CLIENT_ONCE',
'SSL_RECEIVED_SHUTDOWN', 'SSL_SENT_SHUTDOWN',
'SSL_OCSP_NO_STAPLE', 'SSL_OCSP_MUST_STAPLE', 'SSL_OCSP_FAIL_HARD', 'SSL_OCSP_FULL_CHAIN', 'SSL_OCSP_TRY_STAPLE'
]);
}
foreach (@{$options{ssl_opt}}) {
if (/(SSL_[A-Za-z_]+)\s+=>\s*(\S+)/) {
my ($label, $eval) = ($1, $2);
our $assign_var;
if (defined($safe)) {
$safe->reval("\$assign_var = $eval", 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
} else {
eval "\$assign_var = $eval";
}
$ssl_context->{$label} = $assign_var;
}
}
return $ssl_context;
}
sub slurp_file {
my (%options) = @_;
my $content = do {
local $/ = undef;
if (!open my $fh, '<', $options{file}) {
$options{output}->add_option_msg(short_msg => "Could not open file $options{file}: $!");
$options{output}->option_exit();
}
<$fh>;
};
return $content;
}
sub sanitize_command_param {
my (%options) = @_;
return if (!defined($options{value}));
$options{value} =~ s/[`;!&|]//g;
return $options{value};
}
my $security_file = '/etc/centreon-plugins/security.json';
my $whitelist_file = '/etc/centreon-plugins/whitelist.json';
if ($^O eq 'MSWin32') {
$security_file = 'C:/Program Files/centreon-plugins/security.json';
$whitelist_file = 'C:/Program Files/centreon-plugins/whitelist.json';
}
sub check_security_command {
my (%options) = @_;
return 0 if (!(
(defined($options{command}) && $options{command} ne '') ||
(defined($options{command_options}) && $options{command_options} ne '') ||
(defined($options{command_path}) && $options{command_path} ne ''))
);
return 0 if (! -r "$security_file" || -z "$security_file");
my $content = slurp_file(output => $options{output}, file => $security_file);
my $security;
eval {
$security = JSON::XS->new->utf8->decode($content);
};
if ($@) {
$options{output}->add_option_msg(short_msg => 'Cannot decode security file content');
$options{output}->option_exit();
}
if (defined($security->{block_command_overload}) && $security->{block_command_overload} == 1) {
$options{output}->add_option_msg(short_msg => 'Cannot overload command (security)');
$options{output}->option_exit();
}
return 0;
}
sub check_security_whitelist {
my (%options) = @_;
my $command = $options{command};
$command = $options{command_path} . '/' . $options{command} if (defined($options{command_path}) && $options{command_path} ne '');
$command .= ' ' . $options{command_options} if (defined($options{command_options}) && $options{command_options} ne '');
return 0 if (! -r "$security_file" || -z "$security_file");
my $content = slurp_file(output => $options{output}, file => $security_file);
my $security;
eval {
$security = JSON::XS->new->utf8->decode($content);
};
if ($@) {
$options{output}->add_option_msg(short_msg => 'Cannot decode security file content');
$options{output}->option_exit();
}
return 0 if (!defined($security->{whitelist_enabled}) || $security->{whitelist_enabled} !~ /^(?:1|true)$/i);
if (! -r "$whitelist_file") {
$options{output}->add_option_msg(short_msg => 'Cannot read whitelist security file content');
$options{output}->option_exit();
}
if (-z "$whitelist_file") {
$options{output}->add_option_msg(short_msg => 'Cannot execute command (security)');
$options{output}->option_exit();
}
$content = slurp_file(output => $options{output}, file => $whitelist_file);
my $whitelist;
eval {
$whitelist = JSON::XS->new->utf8->decode($content);
};
if ($@) {
$options{output}->add_option_msg(short_msg => 'Cannot decode whitelist security file content');
$options{output}->option_exit();
}
my $matched = 0;
foreach (@$whitelist) {
if ($command =~ /$_/) {
$matched = 1;
last;
}
}
if ($matched == 0) {
$options{output}->add_option_msg(short_msg => 'Cannot execute command (security)');
$options{output}->option_exit();
}
return 0;
}
sub json_decode {
my ($content, %options) = @_;
$content =~ s/\r//mg;
my $object;
my $decoder = JSON::XS->new->utf8;
# this option
if ($options{booleans_as_strings}) {
# boolean_values() is not available on old versions of JSON::XS (Alma 8 still provides v3.04)
if (JSON::XS->can('boolean_values')) {
$decoder = $decoder->boolean_values("false", "true");
} else {
# if boolean_values is not available, perform a dirty substitution of booleans
$content =~ s/"(\w+)"\s*:\s*(true|false)(\s*,?)/"$1": "$2"$3/gm;
}
}
eval {
$object = $decoder->decode($content);
};
if ($@) {
print STDERR "Cannot decode JSON string: $@" . "\n";
return undef;
}
return $object;
}
sub json_encode {
my ($object) = @_;
$object =~ s/\r//mg;
my $encoded;
eval {
$encoded = encode_json($object);
};
if ($@) {
print STDERR 'Cannot encode object to JSON. Error message: ' . $@;
return undef;
}
return $encoded;
}
sub is_local_ip($) {
my ($ip) = @_;
return 0 unless $ip;
return 1 if $ip =~ /^127\./;
return 1 if $ip =~ /^10\./;
return 1 if $ip =~ /^192\.168\./;
return 1 if $ip =~ /^172\.(1[6-9]|2[0-9]|3[0-1])\./;
return 1 if $ip =~ /^169\.254\./;
return 1 if $ip eq '0.0.0.0';
return 0;
}
# This function is used with "sort", it sorts an array of IP addresses.
# $_[0] and $_[1] correspond to Perl's special variables $a and $b used by sort.
# I can't use $a and $b directly here, otherwise Perl generates a warning: "uninitialized value".
sub sort_ips($$) {
my @a = split /\./, $_[0];
my @b = split /\./, $_[1];
return $a[0] <=> $b[0] || $a[1] <=> $b[1] || $a[2] <=> $b[2] || $a[3] <=> $b[3]
}
# function to assess if a string has to be excluded given an include regexp and an exclude regexp
sub is_excluded {
my ($string, $include_regexp, $exclude_regexp) = @_;
return 1 unless defined($string);
return 1 if (defined($exclude_regexp) && $exclude_regexp ne '' && $string =~ /$exclude_regexp/);
return 0 if (!defined($include_regexp) || $include_regexp eq '' || $string =~ /$include_regexp/);
return 1;
}
1;
=head1 NAME
centreon::plugins::misc - A collection of miscellaneous utility functions for Centreon plugins.
=head1 SYNOPSIS
use centreon::plugins::misc;
my $result = centreon::plugins::misc::execute(
command => 'ls',
command_options => '-l'
);
=head1 DESCRIPTION
The `centreon::plugins::misc` module provides a variety of utility functions that can be used in Centreon plugins. These functions include command execution, string manipulation, file handling, and more.
=head1 METHODS
=head2 execute
my $result = centreon::plugins::misc::execute(%options);
Executes a command and returns the result.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to execute.
=item * C<command_options> - Options for the command.
=item * C<timeout> - Timeout for the command execution.
=back
=back
=head2 windows_execute
my ($stdout, $exit_code) = centreon::plugins::misc::windows_execute(%options);
Executes a command on Windows and returns the output and exit code.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to execute.
=item * C<command_options> - Options for the command.
=item * C<timeout> - Timeout for the command execution.
=back
=back
=head2 unix_execute
my $stdout = centreon::plugins::misc::unix_execute(%options);
Executes a command on Unix and returns the output.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to execute.
=item * C<command_options> - Options for the command.
=item * C<timeout> - Timeout for the command execution.
=item * C<wait_exit> - bool.
=item * C<redirect_stderr> - bool.
=item * C<sudo> - bool prepend sudo to the command executed.
=item * C<no_shell_interpretation> - bool don't use sh interpolation on command executed
=back
=back
=head2 mymodule_load
my $result = centreon::plugins::misc::mymodule_load(%options);
Loads a Perl module dynamically.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<module> - The module to load.
=item * C<error_msg> - Error message to display if the module cannot be loaded.
=back
=back
=head2 backtick
my ($status, $output, $exit_code) = centreon::plugins::misc::backtick(%options);
Executes a command using backticks and returns the status, output, and exit code.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to execute.
=item * C<arguments> - Arguments for the command.
=item * C<timeout> - Timeout for the command execution.
=back
=back
=head2 is_empty
my $is_empty = centreon::plugins::misc::is_empty($value);
Checks if a value is empty.
=over 4
=item * C<$value> - The value to check.
=back
=head2 trim
my $trimmed_value = centreon::plugins::misc::trim($value);
Trims whitespace from a string.
=over 4
=item * C<$value> - The string to trim.
=back
=head2 powershell_encoded
my $encoded = centreon::plugins::misc::powershell_encoded($value);
Encodes a string for use in PowerShell.
=over 4
=item * C<$value> - The string to encode.
=back
=head2 powershell_escape
my $escaped = centreon::plugins::misc::powershell_escape($value);
Escapes special characters in a string for use in PowerShell.
=over 4
=item * C<$value> - The string to escape.
=back
=head2 minimal_version
my $is_minimal = centreon::plugins::misc::minimal_version($version_src, $version_dst);
Checks if a version is at least a specified version.
=over 4
=item * C<$version_src> - The source version.
=item * C<$version_dst> - The destination version.
=back
=head2 change_seconds
my $formatted_time = centreon::plugins::misc::change_seconds(%options);
Converts seconds into a human-readable format.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The number of seconds.
=item * C<start> - The starting unit.
=back
=back
=head2 scale_bytesbit
my $scaled_value = centreon::plugins::misc::scale_bytesbit(%options);
Scales a value between bytes and bits.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The value to scale.
=item * C<src_unit> - The source unit.
=item * C<dst_unit> - The destination unit.
=back
=back
=head2 convert_bytes
my $bytes = centreon::plugins::misc::convert_bytes(%options);
Converts a value to bytes.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The value to convert.
=item * C<unit> - The unit of the value.
=back
=back
=head2 convert_fahrenheit
my $celsius = centreon::plugins::misc::convert_fahrenheit(%options);
Converts a temperature from Fahrenheit to Celsius.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The temperature in Fahrenheit.
=back
=back
=head2 expand_exponential
my $expanded = centreon::plugins::misc::expand_exponential(%options);
Expands an exponential value to its full form.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The exponential value.
=back
=back
=head2 alert_triggered
my $is_triggered = centreon::plugins::misc::alert_triggered(%options);
Checks if an alert is triggered based on thresholds.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The value to check.
=item * C<warning> - The warning threshold.
=item * C<critical> - The critical threshold.
=back
=back
=head2 parse_threshold
my ($status, $threshold) = centreon::plugins::misc::parse_threshold(%options);
Parses a threshold string.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<threshold> - The threshold string.
=back
=back
=head2 get_threshold_litteral
my $threshold_str = centreon::plugins::misc::get_threshold_litteral(%options);
Returns the literal representation of a threshold.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<arobase> - Indicates if the threshold is inclusive.
=item * C<start> - The start of the threshold.
=item * C<end> - The end of the threshold.
=back
=back
=head2 set_timezone
my $timezone = centreon::plugins::misc::set_timezone(%options);
Sets the timezone.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<name> - The name of the timezone.
=back
=back
=head2 uniq
my @unique = centreon::plugins::misc::uniq(@values);
Returns a list of unique values.
=over 4
=item * C<@values> - The list of values.
=back
=head2 eval_ssl_options
my $ssl_context = centreon::plugins::misc::eval_ssl_options(%options);
Evaluates SSL options.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<ssl_opt> - The SSL options.
=back
=back
=head2 slurp_file
my $content = centreon::plugins::misc::slurp_file(%options);
Reads the content of a file.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<file> - The file to read.
=back
=back
=head2 sanitize_command_param
my $sanitized = centreon::plugins::misc::sanitize_command_param(%options);
Sanitizes a command parameter.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<value> - The value to sanitize.
=back
=back
=head2 check_security_command
my $status = centreon::plugins::misc::check_security_command(%options);
Checks the security of a command.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to check.
=item * C<command_options> - Options for the command.
=back
=back
=head2 check_security_whitelist
my $status = centreon::plugins::misc::check_security_whitelist(%options);
Checks if a command is in the security whitelist.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<command> - The command to check.
=item * C<command_options> - Options for the command.
=back
=back
=head2 json_decode
my $decoded = centreon::plugins::misc::json_decode($content, %options);
Decodes a JSON string.
=over 4
=item * C<$content> - The JSON string to decode and transform into an object.
=item * C<%options> - Options passed to the function.
=over 4
=item * C<booleans_as_strings> - Defines whether booleans must be converted to C<true>/C<false> strings instead of
JSON:::PP::Boolean values. C<1> => strings, C<0> => booleans.
=back
=back
=head2 json_encode
my $encoded = centreon::plugins::misc::json_encode($object);
Encodes an object to a JSON string.
=over 4
=item * C<$object> - The object to encode.
=back
=head2 is_local_ip
my $is_local = centreon::plugins::misc::is_local_ip($ip);
Returns 1 if an IPv4 IP is within a local address range.
=over 4
=item * C<$ip> - IP to test.
=back
=head2 sort_ips
my @array = ( '192.168.0.3', '127.0.0.1' );
@array = sort centreon::plugins::misc::sort_ips @array;
Returns a sorted array.
=over 4
=item * C<@array> - An array containing IPs to be sorted.
=back
=head2 is_excluded
my $excluded = is_excluded($string, $include_regexp, $exclude_regexp);
Determines whether a string should be excluded based on include and exclude regular expressions.
=over 4
=item * C<$string> - The string to evaluate. If undefined, the function returns 1 (excluded).
=item * C<$include_regexp> - A regular expression to include the string.
=item * C<$exclude_regexp> - A regular expression to exclude the string. If defined and matches the string, the function returns 1 (excluded).
=back
Returns 1 if the string is excluded, 0 if it is included.
The string is excluded if $exclude_regexp is defined and matches the string, or if $include_regexp is defined and does
not match the string. The string will also be excluded if it is undefined.
=head1 AUTHOR
Centreon
=head1 LICENSE
Licensed under the Apache License, Version 2.0.
=cut
CENTREON_PLUGINS_MISC
$fatpacked{"centreon/plugins/mode.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MODE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::mode;
use strict;
use warnings;
use centreon::plugins::perfdata;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{perfdata} = centreon::plugins::perfdata->new(output => $options{output});
%{$self->{option_results}} = ();
@{$self->{option_extras}} = @{$options{options}->{extra_arguments}};
$self->{output} = $options{output};
$self->{output}->use_new_perfdata(value => 1)
if (defined($options{force_new_perfdata}) && $options{force_new_perfdata} == 1);
$self->{mode} = $options{mode};
$self->{version} = '1.0';
return $self;
}
sub init {
my ($self, %options) = @_;
# options{default} = { mode_xxx => { option_name => option_value }, }
%{$self->{option_results}} = %{$options{option_results}};
# Manage default value
return if (!defined($options{default}));
foreach (keys %{$options{default}}) {
if ($_ eq $self->{mode}) {
foreach my $value (keys %{$options{default}->{$_}}) {
if (!defined($self->{option_results}->{$value})) {
$self->{option_results}->{$value} = $options{default}->{$_}->{$value};
}
}
}
}
}
sub version {
my ($self, %options) = @_;
$self->{output}->add_option_msg(short_msg => "Mode Version: " . $self->{version});
}
sub disco_format {
my ($self, %options) = @_;
}
sub disco_show {
my ($self, %options) = @_;
}
1;
CENTREON_PLUGINS_MODE
$fatpacked{"centreon/plugins/multi.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_MULTI';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::multi;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$options{options}->add_options(arguments => {
'modes-exec:s' => { name => 'modes_exec' },
'option-mode:s@' => { name => 'option_mode' }
});
$self->{options} = $options{options};
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (!defined($self->{option_results}->{modes_exec})) {
$self->{output}->add_option_msg(short_msg => "Need to specify --modes-exec option.");
$self->{output}->option_exit();
}
$self->{options_mode_extra} = {};
if (defined($self->{option_results}->{option_mode})) {
foreach (@{$self->{option_results}->{option_mode}}) {
next if (! /^(.+?),(.*)$/);
$self->{options_mode_extra}->{$1} = [] if (!defined($self->{options_mode_extra}->{$1}));
push @{$self->{options_mode_extra}->{$1}}, $2;
}
}
$self->{modes} = $options{modes};
}
sub run {
my ($self, %options) = @_;
$self->{output}->parameter(attr => 'nodisplay', value => 1);
$self->{output}->parameter(attr => 'noexit_die', value => 1);
$self->{output}->use_new_perfdata(value => 1);
my @modes = split /,/, $self->{option_results}->{modes_exec};
foreach (@modes) {
next if (!defined($self->{modes}->{$_}));
eval {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => $self->{modes}->{$_},
error_msg => "Cannot load module --mode $_"
);
@ARGV = (@{$self->{options_mode_extra}->{$_}}) if (defined($self->{options_mode_extra}->{$_}));
$self->{output}->mode(name => $_);
my $mode = $self->{modes}->{$_}->new(options => $self->{options}, output => $self->{output}, mode => $_);
$self->{options}->parse_options();
my $option_results = $self->{options}->get_options();
$mode->check_options(option_results => $option_results, %options);
$mode->run(%options);
};
if ($@) {
$self->{output}->output_add(long_msg => 'eval result mode ' . $_ . ': ' . $@, debug => 1);
}
}
$self->{output}->mode(name => 'multi');
$self->{output}->parameter(attr => 'nodisplay', value => 0);
$self->{output}->parameter(attr => 'noexit_die', value => 0);
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check multiple modes at once.
=over 8
=item B<--modes-exec>
Modes to use, separated by commas.
Example for linux: --modes-exec=cpu,memory,storage,interfaces
=item B<--option-mode>
Define options for the modes selected in --modes-exec.
The option can be used several times.
E.g.: to define two options for the interfaces mode and one for the storage mode:
--option-mode='interfaces,--statefile-dir=/tmp' --option-mode='interfaces,--add-traffic' --option-mode='storage,--statefile-dir=/tmp'
=back
=cut
CENTREON_PLUGINS_MULTI
$fatpacked{"centreon/plugins/options.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_OPTIONS';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::options;
use Pod::Usage;
use strict;
use warnings;
my $alternative = 1;
sub new {
my ($class) = @_;
my $self = {};
bless $self, $class;
$self->{pod_where_loaded} = 0;
$self->{sanity} = 0;
$self->{options_stored} = {};
$self->{options} = {};
@{$self->{pod_package}} = ();
$self->{pod_packages_once} = {};
$self->{extra_arguments} = [];
if ($alternative == 0) {
require Getopt::Long;
Getopt::Long->import();
Getopt::Long::Configure("pass_through");
Getopt::Long::Configure('bundling');
Getopt::Long::Configure('no_auto_abbrev');
} else {
require centreon::plugins::alternative::Getopt;
$centreon::plugins::alternative::Getopt::warn_message = 0;
centreon::plugins::alternative::Getopt->import();
}
return $self;
}
sub set_sanity {
my ($self, %options) = @_;
if ($alternative == 0) {
Getopt::Long::Configure('no_pass_through');
} else {
$centreon::plugins::alternative::Getopt::warn_message = 1;
}
$self->{sanity} = 1;
}
sub set_output {
my ($self, %options) = @_;
$self->{output} = $options{output};
}
sub display_help {
my ($self, %options) = @_;
my $stdout;
foreach (@{$self->{pod_package}}) {
my $where = $self->pod_where(package => $_->{package});
{
local *STDOUT;
open STDOUT, '>', \$stdout;
pod2usage(
-exitval => 'NOEXIT', -input => $where,
-verbose => 99,
-sections => $_->{sections}
) if (defined($where));
}
$self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
}
}
sub add_help {
my ($self, %options) = @_;
# $options{package} = string package
# $options{sections} = string sections
# $options{help_first} = put at the beginning
# $options{once} = put help only one time for a package
if (defined($options{once}) && defined($self->{pod_packages_once}->{$options{package}})) {
return ;
}
if (defined($options{help_first})) {
unshift @{$self->{pod_package}}, {package => $options{package}, sections => $options{sections}};
} else {
push @{$self->{pod_package}}, { package => $options{package}, sections => $options{sections} };
}
$self->{pod_packages_once}->{$options{package}} = 1;
}
sub add_options {
my ($self, %options) = @_;
# $options{arguments} = ref to hash table with string and name to store (example: { 'mode:s' => { name => 'mode', default => 'defaultvalue' )
foreach (keys %{$options{arguments}}) {
if (defined($options{arguments}->{$_}->{redirect})) {
$self->{options}->{$_} = \$self->{options_stored}->{$options{arguments}->{$_}->{redirect}};
next;
}
if (defined($options{arguments}->{$_}->{default})) {
$self->{options_stored}->{$options{arguments}->{$_}->{name}} = $options{arguments}->{$_}->{default};
} else {
$self->{options_stored}->{$options{arguments}->{$_}->{name}} = undef;
}
$self->{options}->{$_} = \$self->{options_stored}->{$options{arguments}->{$_}->{name}};
}
}
sub parse_options {
my $self = shift;
#%{$self->{options_stored}} = ();
my $save_warn_handler;
if ($self->{sanity} == 1) {
$save_warn_handler = $SIG{__WARN__};
$SIG{__WARN__} = sub {
$self->{output}->add_option_msg(short_msg => $_[0]);
$self->{output}->option_exit(nolabel => 1);
};
}
# Store all arguments placed after the special argument "--" in the 'extra_arguments' list
$self->{options}->{'_double_dash_'} = \$self->{extra_arguments};
GetOptions(
%{$self->{options}}
);
%{$self->{options}} = ();
$SIG{__WARN__} = $save_warn_handler if ($self->{sanity} == 1);
}
sub pod_where {
my ($self, %options) = @_;
if ($self->{pod_where_loaded} == 0) {
$self->{pod_where_loaded} = 1;
my ($code) = centreon::plugins::misc::mymodule_load(
module => 'Pod::Find',
no_quit => 1
);
if ($code) {
$code = centreon::plugins::misc::mymodule_load(
module => 'Pod::Simple::Search',
no_quit => 1
);
die "Cannot load module 'Pod::Simple::Search'" if ($code);
$self->{pod_where_loaded} = 2;
$self->{pod_simple_search} = Pod::Simple::Search->new();
$self->{pod_simple_search}->inc(1);
}
}
if ($self->{pod_where_loaded} == 1) {
return Pod::Find::pod_where({-inc => 1}, $options{package});
}
return $self->{pod_simple_search}->find($options{package});
}
sub get_option {
my ($self, %options) = @_;
return $self->{options_stored}->{$options{argument}};
}
sub get_options {
my $self = shift;
return $self->{options_stored};
}
sub clean {
my $self = shift;
$self->{options_stored} = {};
}
1;
CENTREON_PLUGINS_OPTIONS
$fatpacked{"centreon/plugins/output.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_OUTPUT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::output;
use strict;
use warnings;
use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{options})) {
print "Class Output: Need to specify 'options' argument to load.\n";
exit 3;
}
$options{options}->add_options(arguments => {
'explode-perfdata-max:s@' => { name => 'explode_perfdata_max' },
'range-perfdata:s' => { name => 'range_perfdata' },
'filter-perfdata:s' => { name => 'filter_perfdata' },
'filter-perfdata-adv:s' => { name => 'filter_perfdata_adv' },
'change-perfdata:s@' => { name => 'change_perfdata' },
'extend-perfdata:s@' => { name => 'extend_perfdata' },
'extend-perfdata-group:s@'=> { name => 'extend_perfdata_group' },
'change-exit:s@' => { name => 'change_exit' },
'change-short-output:s@' => { name => 'change_short_output' },
'change-long-output:s@' => { name => 'change_long_output' },
'change-output-adv:s@' => { name => 'change_output_adv' },
'use-new-perfdata' => { name => 'use_new_perfdata' },
'filter-uom:s' => { name => 'filter_uom' },
'verbose' => { name => 'verbose' },
'debug' => { name => 'debug' },
'debug-stream' => { name => 'debug_stream' },
'opt-exit:s' => { name => 'opt_exit', default => 'unknown' },
'output-xml' => { name => 'output_xml' },
'output-json' => { name => 'output_json' },
'output-ignore-perfdata' => { name => 'output_ignore_perfdata' },
'output-ignore-label' => { name => 'output_ignore_label' },
'output-openmetrics' => { name => 'output_openmetrics' },
'output-file:s' => { name => 'output_file' },
'disco-format' => { name => 'disco_format' },
'disco-show' => { name => 'disco_show' },
'float-precision:s' => { name => 'float_precision', default => 8 },
'source-encoding:s' => { name => 'source_encoding' , default => 'UTF-8' }
});
$self->{option_results} = {};
$self->{option_msg} = [];
$self->{nodisplay} = 0;
$self->{noexit_die} = 0;
$self->{is_output_xml} = 0;
$self->{is_output_json} = 0;
$self->{errors} = {OK => 0, WARNING => 1, CRITICAL => 2, UNKNOWN => 3, PENDING => 4};
$self->{errors_num} = {0 => 'OK', 1 => 'WARNING', 2 => 'CRITICAL', 3 => 'UNKNOWN', 4 => 'PENDING'};
$self->{myerrors} = {0 => "OK", 1 => "WARNING", 3 => "UNKNOWN", 7 => "CRITICAL"};
$self->{myerrors_mask} = {CRITICAL => 7, WARNING => 1, UNKNOWN => 3, OK => 0};
$self->{global_short_concat_outputs} = {OK => undef, WARNING => undef, CRITICAL => undef, UNKNOWN => undef, UNQUALIFIED_YET => undef};
$self->{global_short_outputs} = {OK => [], WARNING => [], CRITICAL => [], UNKNOWN => [], UNQUALIFIED_YET => []};
$self->{global_long_output} = [];
$self->{perfdatas} = [];
$self->{explode_perfdatas} = {};
$self->{change_perfdata} = {};
$self->{explode_perfdata_total} = 0;
$self->{range_perfdata} = 0;
$self->{global_status} = 0;
$self->{encode_import} = 0;
$self->{perlqq} = 0;
$self->{safe_test} = 0;
$self->{disco_elements} = [];
$self->{disco_entries} = [];
$self->{plugin} = '';
$self->{mode} = '';
return $self;
}
sub check_options {
my ($self, %options) = @_;
# $options{option_results} = ref to options result
%{$self->{option_results}} = %{$options{option_results}};
$self->{option_results}->{opt_exit} = lc($self->{option_results}->{opt_exit});
if (!$self->is_litteral_status(status => $self->{option_results}->{opt_exit})) {
$self->add_option_msg(short_msg => "Unknown value '" . $self->{option_results}->{opt_exit} . "' for --opt-exit.");
$self->option_exit(exit_litteral => 'unknown');
}
# Go in XML Mode
if ($self->is_disco_show() || $self->is_disco_format()) {
# By Default XML
if (!defined($self->{option_results}->{output_json})) {
$self->{option_results}->{output_xml} = 1;
}
}
if (defined($self->{option_results}->{range_perfdata})) {
$self->{range_perfdata} = $self->{option_results}->{range_perfdata};
$self->{range_perfdata} = 1 if ($self->{range_perfdata} eq '');
if ($self->{range_perfdata} !~ /^[012]$/) {
$self->add_option_msg(short_msg => "Wrong range-perfdata option '" . $self->{range_perfdata} . "'");
$self->option_exit();
}
}
if (defined($self->{option_results}->{change_output_adv})) {
foreach (@{$self->{option_results}->{change_output_adv}}) {
my ($expr, $short_output, $exit_code) = split /,/;
next if (!defined($expr) || $expr eq '');
$expr =~ s/%\{(.*?)\}/\$values->{$1}/g;
$expr =~ s/%\((.*?)\)/\$values->{$1}/g;
if ( defined($exit_code) && $exit_code ne '' && defined( $self->{errors}->{uc($exit_code)} ) ) {
$exit_code = uc($exit_code);
} else {
$exit_code = undef;
}
$self->{change_output_adv} = [] if (!defined($self->{change_output_adv}));
push @{$self->{change_output_adv}}, {
expr => $expr,
short_output => $short_output,
exit_code => $exit_code
};
}
}
if (defined($self->{option_results}->{change_exit})) {
$self->{change_exit} = {};
foreach (@{$self->{option_results}->{change_exit}}) {
my ($src, $dst) = split(/=/);
next if (!defined($src) || !defined($self->{errors}->{ uc($src) }));
next if (!defined($dst) || !defined($self->{errors}->{ uc($dst) }));
$self->{change_exit}->{uc($src)} = uc($dst);
}
}
if (defined($self->{option_results}->{explode_perfdata_max})) {
if (${$self->{option_results}->{explode_perfdata_max}}[0] eq '') {
$self->{explode_perfdata_total} = 2;
} else {
$self->{explode_perfdata_total} = 1;
foreach (@{$self->{option_results}->{explode_perfdata_max}}) {
my ($perf_match, $perf_result) = split /,/;
if (!defined($perf_result)) {
$self->add_option_msg(short_msg => "Wrong explode-perfdata-max option '" . $_ . "' (syntax: match,value)");
$self->option_exit();
}
$self->{explode_perfdatas}->{$perf_match} = $perf_result;
}
}
}
if (defined($self->{option_results}->{filter_perfdata_adv}) && $self->{option_results}->{filter_perfdata_adv} ne '') {
$self->{option_results}->{filter_perfdata_adv} =~ s/%\{(.*?)\}/\$values->{$1}/g;
$self->{option_results}->{filter_perfdata_adv} =~ s/%\((.*?)\)/\$values->{$1}/g;
$self->{option_results}->{filter_perfdata_adv} =~ s/alert_triggered\(\)/alert_triggered\(%\$values\)/g;
}
$self->load_perfdata_extend_args();
$self->{option_results}->{use_new_perfdata} = 1 if (defined($self->{option_results}->{output_openmetrics}));
$self->{source_encoding} = (!defined($self->{option_results}->{source_encoding}) || $self->{option_results}->{source_encoding} eq '') ?
'UTF-8' : $self->{option_results}->{source_encoding};
}
sub add_option_msg {
my ($self, %options) = @_;
# $options{short_msg} = string msg
# $options{long_msg} = string msg
$options{severity} = 'UNQUALIFIED_YET';
$self->output_add(%options);
}
sub set_ignore_label {
my ($self, %options) = @_;
$self->{option_results}->{output_ignore_label} = 1;
}
sub set_status {
my ($self, %options) = @_;
# $options{exit_litteral} = string litteral exit
# Nothing to do for 'UNQUALIFIED_YET'
if (!$self->{myerrors_mask}->{uc($options{exit_litteral})}) {
return ;
}
$self->{global_status} |= $self->{myerrors_mask}->{uc($options{exit_litteral})};
}
sub output_add {
my ($self, %params) = @_;
my %args = (
severity => 'OK',
separator => ' - ',
debug => 0,
short_msg => undef,
long_msg => undef,
);
my $options = { %args, %params };
if (defined($options->{short_msg})) {
chomp $options->{short_msg};
if (defined($self->{global_short_concat_outputs}->{uc($options->{severity})})) {
$self->{global_short_concat_outputs}->{uc($options->{severity})} .= $options->{separator} . $options->{short_msg};
} else {
$self->{global_short_concat_outputs}->{uc($options->{severity})} = $options->{short_msg};
}
push @{$self->{global_short_outputs}->{uc($options->{severity})}}, $options->{short_msg};
$self->set_status(exit_litteral => $options->{severity});
}
if (defined($options->{long_msg})) {
chomp $options->{long_msg};
push @{$self->{global_long_output}}, $options->{long_msg} if ($options->{debug} == 0 || defined($self->{option_results}->{debug}));
print $options->{long_msg} . "\n" if (defined($self->{option_results}->{debug_stream}));
}
}
sub perfdata_add {
my ($self, %options) = @_;
my $perfdata = {
label => '', value => '', unit => '', warning => '', critical => '', min => '', max => '', mode => $self->{mode}
};
foreach (keys %options) {
next if (!defined($options{$_}));
$perfdata->{$_} = $options{$_};
}
if ((defined($self->{option_results}->{use_new_perfdata}) || defined($options{force_new_perfdata})) &&
defined($options{nlabel})) {
$perfdata->{label} = $options{nlabel};
}
if (defined($options{instances})) {
$options{instances} = [$options{instances}] if (!ref($options{instances}));
my ($external_instance_separator, $internal_instance_separator) = ('#', '~');
if (defined($self->{option_results}->{use_new_perfdata}) || defined($options{force_new_perfdata})) {
$perfdata->{label} = join('~', @{$options{instances}}) . '#' . $perfdata->{label};
} else {
$perfdata->{label} .= '_' . join('_', @{$options{instances}});
}
}
$perfdata->{label} =~ s/'/''/g;
push @{$self->{perfdatas}}, $perfdata;
}
sub filter_perfdata {
my ($self, %options) = @_;
return 1 if (
defined($self->{option_results}->{filter_perfdata}) &&
$options{perf}->{label} !~ /$self->{option_results}->{filter_perfdata}/
);
return 1 if (
defined($self->{option_results}->{filter_perfdata_adv}) &&
$self->{option_results}->{filter_perfdata_adv} ne '' &&
!$self->test_eval(test => $self->{option_results}->{filter_perfdata_adv}, values => $options{perf})
);
return 0;
}
sub range_perfdata {
my ($self, %options) = @_;
return if ($self->{range_perfdata} == 0);
if ($self->{range_perfdata} == 1) {
for (my $i = 0; $i < scalar(@{$options{ranges}}); $i++) {
${${$options{ranges}}[$i]} =~ s/^(@?)-?[0\.]+:/$1/;
}
} else {
for (my $i = 0; $i < scalar(@{$options{ranges}}); $i++) {
${${$options{ranges}}[$i]} = '';
}
}
}
sub output_json {
my ($self, %options) = @_;
my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
my $json_content = {
plugin => {
name => $self->{plugin},
mode => $self->{mode},
exit => $options{exit_litteral},
outputs => [],
perfdatas => []
}
};
foreach my $code_litteral (keys %{$self->{global_short_outputs}}) {
foreach (@{$self->{global_short_outputs}->{$code_litteral}}) {
my ($child_output, $child_type, $child_msg, $child_exit);
my $lcode_litteral = ($code_litteral eq 'UNQUALIFIED_YET' ? uc($options{exit_litteral}) : $code_litteral);
push @{$json_content->{plugin}->{outputs}}, {
type => 1,
msg => ($options{nolabel} == 0 ? ($lcode_litteral . ': ') : '') . $_,
exit => $lcode_litteral
};
}
}
if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
foreach (@{$self->{global_long_output}}) {
push @{$json_content->{plugin}->{outputs}}, {
type => 2,
msg => $_
};
}
}
if ($options{force_ignore_perfdata} == 0) {
$self->change_perfdata();
foreach my $perf (@{$self->{perfdatas}}) {
next if ($self->filter_perfdata(perf => $perf));
$self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
my %values = ();
foreach my $key (keys %$perf) {
$perf->{$key} = '' if (defined($self->{option_results}->{filter_uom}) && $key eq 'unit' &&
$perf->{$key} !~ /$self->{option_results}->{filter_uom}/);
$values{$key} = $perf->{$key};
}
push @{$json_content->{plugin}->{perfdatas}}, {
%values
};
}
}
print $self->{json_output}->encode($json_content);
}
sub output_xml {
my ($self, %options) = @_;
my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
my ($child_plugin_name, $child_plugin_mode, $child_plugin_exit, $child_plugin_output, $child_plugin_perfdata);
my $root = $self->{xml_output}->createElement('plugin');
$self->{xml_output}->setDocumentElement($root);
$child_plugin_name = $self->{xml_output}->createElement('name');
$child_plugin_name->appendText($self->{plugin});
$child_plugin_mode = $self->{xml_output}->createElement('mode');
$child_plugin_mode->appendText($self->{mode});
$child_plugin_exit = $self->{xml_output}->createElement('exit');
$child_plugin_exit->appendText($options{exit_litteral});
$child_plugin_output = $self->{xml_output}->createElement('outputs');
$child_plugin_perfdata = $self->{xml_output}->createElement('perfdatas');
$root->addChild($child_plugin_name);
$root->addChild($child_plugin_mode);
$root->addChild($child_plugin_exit);
$root->addChild($child_plugin_output);
$root->addChild($child_plugin_perfdata);
foreach my $code_litteral (keys %{$self->{global_short_outputs}}) {
foreach (@{$self->{global_short_outputs}->{$code_litteral}}) {
my ($child_output, $child_type, $child_msg, $child_exit);
my $lcode_litteral = ($code_litteral eq 'UNQUALIFIED_YET' ? uc($options{exit_litteral}) : $code_litteral);
$child_output = $self->{xml_output}->createElement('output');
$child_plugin_output->addChild($child_output);
$child_type = $self->{xml_output}->createElement('type');
$child_type->appendText(1); # short
$child_msg = $self->{xml_output}->createElement('msg');
$child_msg->appendText(($options{nolabel} == 0 ? ($lcode_litteral . ': ') : '') . $_);
$child_exit = $self->{xml_output}->createElement('exit');
$child_exit->appendText($lcode_litteral);
$child_output->addChild($child_type);
$child_output->addChild($child_exit);
$child_output->addChild($child_msg);
}
}
if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
foreach (@{$self->{global_long_output}}) {
my ($child_output, $child_type, $child_msg);
$child_output = $self->{xml_output}->createElement('output');
$child_plugin_output->addChild($child_output);
$child_type = $self->{xml_output}->createElement('type');
$child_type->appendText(2); # long
$child_msg = $self->{xml_output}->createElement('msg');
$child_msg->appendText($_);
$child_output->addChild($child_type);
$child_output->addChild($child_msg);
}
}
if ($options{force_ignore_perfdata} == 0) {
$self->change_perfdata();
foreach my $perf (@{$self->{perfdatas}}) {
next if ($self->filter_perfdata(perf => $perf));
$self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
my ($child_perfdata);
$child_perfdata = $self->{xml_output}->createElement('perfdata');
$child_plugin_perfdata->addChild($child_perfdata);
foreach my $key (keys %$perf) {
$perf->{$key} = '' if (defined($self->{option_results}->{filter_uom}) && $key eq 'unit' &&
$perf->{$key} !~ /$self->{option_results}->{filter_uom}/);
my $child = $self->{xml_output}->createElement($key);
$child->appendText($perf->{$key});
$child_perfdata->addChild($child);
}
}
}
print $self->{xml_output}->toString(1);
}
sub output_openmetrics {
my ($self, %options) = @_;
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'Time::HiRes',
error_msg => "Cannot load module 'Time::HiRes'."
);
my $time_ms = int(Time::HiRes::time() * 1000);
$self->change_perfdata();
foreach my $perf (@{$self->{perfdatas}}) {
next if ($self->filter_perfdata(perf => $perf));
$perf->{unit} = '' if (
defined($self->{option_results}->{filter_uom}) &&
$perf->{unit} !~ /$self->{option_results}->{filter_uom}/
);
$self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
my $label = $perf->{label};
my $instance;
if ($label =~ /^(.*?)#(.*)$/) {
($perf->{instance}, $label) = ($1, $2);
}
my ($bucket, $append) = ('{plugin="' . $self->{plugin} . '",mode="' . $perf->{mode} . '"', '');
foreach ('unit', 'warning', 'critical', 'min', 'max', 'instance') {
if (defined($perf->{$_}) && $perf->{$_} ne '') {
$bucket .= ',' . $_ . '="' . $perf->{$_} . '"';
}
}
$bucket .= '}';
print $label . $bucket . ' ' . $perf->{value} . ' ' . $time_ms . "\n";
}
}
sub output_txt_short_display {
my ($self, %options) = @_;
if (defined($self->{global_short_concat_outputs}->{CRITICAL})) {
print (($options{nolabel} == 0 ? 'CRITICAL: ' : '') . $self->{global_short_concat_outputs}->{CRITICAL} . " ");
}
if (defined($self->{global_short_concat_outputs}->{WARNING})) {
print (($options{nolabel} == 0 ? 'WARNING: ' : '') . $self->{global_short_concat_outputs}->{WARNING} . " ");
}
if (defined($self->{global_short_concat_outputs}->{UNKNOWN})) {
print (($options{nolabel} == 0 ? 'UNKNOWN: ' : '') . $self->{global_short_concat_outputs}->{UNKNOWN} . " ");
}
if (uc($options{exit_litteral}) eq 'OK') {
print (($options{nolabel} == 0 ? 'OK: ' : '') . (defined($self->{global_short_concat_outputs}->{OK}) ? $self->{global_short_concat_outputs}->{OK} : '') . " ");
}
}
sub output_txt_short {
my ($self, %options) = @_;
if (!defined($self->{option_results}->{change_short_output}) &&
!defined($self->{change_output_adv})) {
$self->output_txt_short_display(%options);
return ;
}
my $stdout = '';
{
local *STDOUT;
open STDOUT, '>', \$stdout;
$self->output_txt_short_display(%options);
}
foreach (@{$self->{option_results}->{change_short_output}}) {
my ($pattern, $replace, $modifier) = split /~/;
next if (!defined($pattern));
$replace = '' if (!defined($replace));
$modifier = '' if (!defined($modifier));
eval "\$stdout =~ s{$pattern}{$replace}$modifier";
}
my $exit = defined($options{exit_litteral}) ? uc($options{exit_litteral}) : uc($self->{myerrors}->{ $self->{global_status} });
foreach (@{$self->{change_output_adv}}) {
if ($self->test_eval(test => $_->{expr}, values => { short_output => $stdout, exit_code => $self->{errors}->{$exit} })) {
if (defined($_->{short_output}) && $_->{short_output} ne '') {
$stdout = $_->{short_output};
}
if (defined($_->{exit_code}) && $_->{exit_code} ne '') {
$self->{coa_save_exit_code} = $_->{exit_code};
}
}
}
print $stdout;
}
sub output_txt {
my ($self, %options) = @_;
my $force_ignore_perfdata = (defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) ? 1 : 0;
my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
return if ($self->{nodisplay} == 1);
if (defined($self->{global_short_concat_outputs}->{UNQUALIFIED_YET})) {
$self->output_add(severity => uc($options{exit_litteral}), short_msg => $self->{global_short_concat_outputs}->{UNQUALIFIED_YET});
}
$self->output_txt_short(%options);
if ($force_ignore_perfdata == 0) {
my $pipe = 0;
$self->change_perfdata();
foreach my $perf (@{$self->{perfdatas}}) {
next if ($self->filter_perfdata(perf => $perf));
$perf->{unit} = '' if (defined($self->{option_results}->{filter_uom}) &&
$perf->{unit} !~ /$self->{option_results}->{filter_uom}/);
$self->range_perfdata(ranges => [\$perf->{warning}, \$perf->{critical}]);
if ($pipe == 0) {
print '|';
$pipe = 1;
}
print " '" . $perf->{label} . "'=" . $perf->{value} . $perf->{unit} . ';' . $perf->{warning} . ';' . $perf->{critical} . ';' . $perf->{min} . ';' . $perf->{max};
}
}
print "\n";
if (defined($self->{option_results}->{verbose}) || $force_long_output == 1) {
if (scalar(@{$self->{global_long_output}})) {
print join("\n", @{$self->{global_long_output}});
print "\n";
}
}
}
sub change_long_output {
my ($self, %options) = @_;
return if (!(defined($self->{option_results}->{verbose}) || $options{force_long_output} == 1));
return if (!defined($self->{option_results}->{change_long_output}));
my $long_output = join("\n", @{$self->{global_long_output}});
foreach (@{$self->{option_results}->{change_long_output}}) {
my ($pattern, $replace, $modifier) = split /~/;
next if (!defined($pattern));
$replace = '' if (!defined($replace));
$modifier = '' if (!defined($modifier));
eval "\$long_output =~ s{$pattern}{$replace}$modifier";
}
$self->{global_long_output} = [split(/\n/, $long_output)];
}
sub display {
my ($self, %options) = @_;
my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
my $force_ignore_perfdata = ((defined($options{force_ignore_perfdata}) && $options{force_ignore_perfdata} == 1) || $self->{option_results}->{output_ignore_perfdata}) ? 1 : 0;
my $force_long_output = (defined($options{force_long_output}) && $options{force_long_output} == 1) ? 1 : 0;
$force_long_output = 1 if (defined($self->{option_results}->{debug}));
if (defined($self->{option_results}->{output_openmetrics})) {
$self->perfdata_add(nlabel => 'plugin.mode.status', value => $self->{errors}->{$self->{myerrors}->{$self->{global_status}}});
}
if (defined($self->{option_results}->{change_long_output})) {
$self->change_long_output(force_long_output => $force_long_output);
}
return if ($self->{nodisplay} == 1);
if (defined($self->{option_results}->{output_file})) {
if (!open (STDOUT, '>', $self->{option_results}->{output_file})) {
$self->output_add(
severity => 'UNKNOWN',
short_msg => "cannot open file '" . $self->{option_results}->{output_file} . "': $!"
);
}
}
if (defined($self->{option_results}->{output_xml})) {
$self->create_xml_document();
if ($self->{is_output_xml}) {
$self->output_xml(
exit_litteral => $self->get_litteral_status(),
nolabel => $nolabel,
force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
);
return ;
}
} elsif (defined($self->{option_results}->{output_json})) {
$self->create_json_document();
if ($self->{is_output_json}) {
$self->output_json(
exit_litteral => $self->get_litteral_status(),
nolabel => $nolabel,
force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
);
return ;
}
} elsif (defined($self->{option_results}->{output_openmetrics})) {
$self->output_openmetrics();
return ;
}
$self->output_txt(
exit_litteral => $self->get_litteral_status(),
nolabel => $nolabel,
force_ignore_perfdata => $force_ignore_perfdata, force_long_output => $force_long_output
);
}
sub die_exit {
my ($self, %options) = @_;
# $options{exit_litteral} = string litteral exit
# $options{nolabel} = interger label display
my $exit_litteral = defined($options{exit_litteral}) ? $options{exit_litteral} : $self->{option_results}->{opt_exit};
my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
# ignore long output in the following case
$self->{option_results}->{verbose} = undef;
if (defined($self->{option_results}->{output_xml})) {
$self->create_xml_document();
if ($self->{is_output_xml}) {
$self->output_xml(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
$self->exit(exit_litteral => $exit_litteral);
}
} elsif (defined($self->{option_results}->{output_json})) {
$self->create_json_document();
if ($self->{is_output_json}) {
$self->output_json(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
$self->exit(exit_litteral => $exit_litteral);
}
}
$self->output_txt(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1);
$self->exit(exit_litteral => $exit_litteral);
}
sub option_exit {
my ($self, %options) = @_;
# $options{exit_litteral} = string litteral exit
# $options{nolabel} = interger label display
my $exit_litteral = defined($options{exit_litteral}) ? $options{exit_litteral} : $self->{option_results}->{opt_exit};
my $nolabel = (defined($options{nolabel}) || defined($self->{option_results}->{output_ignore_label})) ? 1 : 0;
if (defined($self->{option_results}->{output_xml})) {
$self->create_xml_document();
if ($self->{is_output_xml}) {
$self->output_xml(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
$self->exit(exit_litteral => $exit_litteral);
}
} elsif (defined($self->{option_results}->{output_json})) {
$self->create_json_document();
if ($self->{is_output_json}) {
$self->output_json(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
$self->exit(exit_litteral => $exit_litteral);
}
} elsif (defined($self->{option_results}->{output_openmetrics})) {
$self->set_status(exit_litteral => $exit_litteral);
$self->output_openmetrics();
$self->exit(exit_litteral => $exit_litteral);
}
$self->output_txt(exit_litteral => $exit_litteral, nolabel => $nolabel, force_ignore_perfdata => 1, force_long_output => 1);
$self->exit(exit_litteral => $exit_litteral);
}
sub exit {
my ($self, %options) = @_;
if ($self->{noexit_die} == 1) {
die 'exit';
}
my $exit;
if (defined($options{exit_litteral})) {
$exit = uc($options{exit_litteral});
} else {
$exit = $self->{myerrors}->{ $self->{global_status} };
}
if (defined($self->{coa_save_exit_code})) {
$exit = $self->{coa_save_exit_code};
}
if (defined($self->{change_exit}) && defined($self->{change_exit}->{$exit})) {
$exit = $self->{change_exit}->{$exit};
}
exit $self->{errors}->{$exit};
}
sub get_option {
my ($self, %options) = @_;
return $self->{option_results}->{$options{option}};
}
sub get_most_critical {
my ($self, %options) = @_;
my $current_status = 0; # For 'OK'
foreach (@{$options{status}}) {
if ($self->{myerrors_mask}->{uc($_)} > $current_status) {
$current_status = $self->{myerrors_mask}->{uc($_)};
}
}
return $self->{myerrors}->{$current_status};
}
sub get_litteral_status {
my ($self, %options) = @_;
if (defined($options{status})) {
if (defined($self->{errors_num}->{$options{status}})) {
return $self->{errors_num}->{$options{status}};
}
return $options{status};
} else {
return $self->{myerrors}->{$self->{global_status}};
}
}
sub is_status {
my ($self, %options) = @_;
# $options{value} = string status
# $options{litteral} = value is litteral
# $options{compare} = string status
if (defined($options{litteral})) {
my $value = defined($options{value}) ? $options{value} : $self->get_litteral_status();
if (uc($value) eq uc($options{compare})) {
return 1;
}
return 0;
}
my $value = defined($options{value}) ? $options{value} : $self->{global_status};
my $dec_val = $self->{myerrors_mask}->{$value};
my $lresult = $value & $dec_val;
# Need to manage 0
if ($lresult > 0 || ($dec_val == 0 && $value == 0)) {
return 1;
}
return 0;
}
sub is_litteral_status {
my ($self, %options) = @_;
# $options{status} = string status
if (defined($self->{errors}->{uc($options{status})})) {
return 1;
}
return 0;
}
sub create_json_document {
my ($self) = @_;
if (centreon::plugins::misc::mymodule_load(
no_quit => 1, module => 'JSON',
error_msg => "Cannot load module 'JSON'.")
) {
print "Cannot load module 'JSON'\n";
$self->exit(exit_litteral => 'unknown');
}
$self->{is_output_json} = 1;
$self->{json_output} = JSON->new->utf8();
}
sub create_xml_document {
my ($self) = @_;
if (centreon::plugins::misc::mymodule_load(
no_quit => 1, module => 'XML::LibXML',
error_msg => "Cannot load module 'XML::LibXML'.")
) {
print "Cannot load module 'XML::LibXML'\n";
$self->exit(exit_litteral => 'unknown');
}
$self->{is_output_xml} = 1;
$self->{xml_output} = XML::LibXML::Document->new('1.0', 'utf-8');
}
sub plugin {
my ($self, %options) = @_;
# $options{name} = string name
if (defined($options{name})) {
$self->{plugin} = $options{name};
}
return $self->{plugin};
}
sub mode {
my ($self, %options) = @_;
if (defined($options{name})) {
$self->{mode} = $options{name};
}
return $self->{mode};
}
sub add_disco_format {
my ($self, %options) = @_;
push @{$self->{disco_elements}}, @{$options{elements}};
}
sub display_disco_format {
my ($self, %options) = @_;
if (defined($self->{option_results}->{output_xml})) {
$self->create_xml_document();
my $root = $self->{xml_output}->createElement('data');
$self->{xml_output}->setDocumentElement($root);
foreach (@{$self->{disco_elements}}) {
my $child = $self->{xml_output}->createElement("element");
$child->appendText($_);
$root->addChild($child);
}
print $self->{xml_output}->toString(1);
} elsif (defined($self->{option_results}->{output_json})) {
$self->create_json_document();
my $json_content = {data => [] };
foreach (@{$self->{disco_elements}}) {
push @{$json_content->{data}}, $_;
}
print $self->{json_output}->encode($json_content);
}
}
sub display_disco_show {
my ($self, %options) = @_;
if (defined($self->{option_results}->{output_xml})) {
$self->create_xml_document();
my $root = $self->{xml_output}->createElement('data');
$self->{xml_output}->setDocumentElement($root);
foreach (@{$self->{disco_entries}}) {
my $child = $self->{xml_output}->createElement('label');
foreach my $key (keys %$_) {
# Encode all non printable chars as hexadecimal entities to produce valid XML
# I.e. "test ^H" becomes "test "
my $val = $_->{$key};
$val=~s{([[:cntrl:]])}{"&#x".sprintf("%X",ord($1)).";"}ge;
$child->setAttribute($key, $val);
}
$root->addChild($child);
}
print $self->{xml_output}->toString(1);
} elsif (defined($self->{option_results}->{output_json})) {
$self->create_json_document();
my $json_content = {data => [] };
foreach (@{$self->{disco_entries}}) {
my %values = ();
foreach my $key (keys %$_) {
$values{$key} = $_->{$key};
}
push @{$json_content->{data}}, {%values};
}
print $self->{json_output}->encode($json_content);
}
}
sub decode {
my ($self, $value) = @_;
if ($self->{encode_import} == 0) {
# Some Perl version dont have the following module (like Perl 5.6.x)
my $rv = centreon::plugins::misc::mymodule_load(
no_quit => 1,
module => 'Encode',
error_msg => "Cannot load module 'Encode'."
);
return $value if ($rv);
$self->{encode_import} = 1;
eval '$self->{perlqq} = Encode::PERLQQ';
}
return centreon::plugins::misc::trim(Encode::decode($self->{source_encoding}, $value, $self->{perlqq}));
}
sub parameter {
my ($self, %options) = @_;
if (defined($options{attr})) {
$self->{$options{attr}} = $options{value};
}
return $self->{$options{attr}};
}
sub add_disco_entry {
my ($self, %options) = @_;
push @{$self->{disco_entries}}, {%options};
}
sub is_disco_format {
my ($self) = @_;
if (defined($self->{option_results}->{disco_format})) {
return 1;
}
return 0;
}
sub is_disco_show {
my ($self) = @_;
if (defined($self->{option_results}->{disco_show})) {
return 1;
}
return 0;
}
sub is_verbose {
my ($self) = @_;
if (defined($self->{option_results}->{verbose})) {
return 1;
}
return 0;
}
sub is_debug {
my ($self) = @_;
if (defined($self->{option_results}->{debug}) || defined($self->{option_results}->{debug_stream})) {
return 1;
}
return 0;
}
sub load_eval {
my ($self) = @_;
my ($code) = centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'Safe',
no_quit => 1
);
if ($code == 0) {
$self->{safe} = Safe->new();
$self->{safe}->share('$values');
$self->{safe}->share('$assign_var');
$self->{safe}->share_from('centreon::plugins::misc', ['alert_triggered']);
}
$self->{safe_test} = 1;
}
sub test_eval {
my ($self, %options) = @_;
$self->load_eval() if ($self->{safe_test} == 0);
my $result;
if (defined($self->{safe})) {
our $values = $options{values};
$result = $self->{safe}->reval($options{test}, 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
} elsif (defined($options{values})) {
my $values = $options{values};
{
local $SIG{__WARN__} = sub {}; # ignore
$result = eval "$options{test}";
if ($@) {
die 'Code evaluation error: ' . $@;
}
}
}
return $result;
}
sub open_eval {
my ($self, %options) = @_;
$self->load_eval() if ($self->{safe_test} == 0);
our $values = $options{values};
$self->{safe}->reval("$options{eval}", 1);
return $values;
}
sub assign_eval {
my ($self, %options) = @_;
$self->load_eval() if ($self->{safe_test} == 0);
our $assign_var;
if (defined($self->{safe})) {
our $values = $options{values};
$self->{safe}->reval("\$assign_var = $options{eval}", 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
} else {
my $values = $options{values};
eval "\$assign_var = $options{eval}";
}
return $assign_var;
}
sub use_new_perfdata {
my ($self, %options) = @_;
$self->{option_results}->{use_new_perfdata} = $options{value}
if (defined($options{value}));
if (defined($self->{option_results}->{use_new_perfdata})) {
return 1;
}
return 0;
}
sub get_instance_perfdata_separator {
my ($self) = @_;
if (defined($self->{option_results}->{use_new_perfdata})) {
return '~';
}
return '_';
}
sub parse_pfdata_scale {
my ($self, %options) = @_;
# --extend-perfdata=traffic_in,,scale(Mbps),mbps
my $args = { unit => 'auto' };
if ($options{args} =~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s|auto)$/) {
$args->{quantity} = defined($1) ? $1 : '';
$args->{unit} = $2;
} elsif ($options{args} ne '') {
return 1;
}
return (0, $args);
}
sub parse_pfdata_math {
my ($self, %options) = @_;
# --extend-perfdata=perfx,,math(current + 10 - 100, 1)
my $args = { math => undef, apply_threshold => 0 };
my ($math, $apply_threshold) = split /\|/, $options{args};
if ($math =~ /^((?:[\s\.\-\+\*\/0-9\(\)]|current)+)$/) {
$args->{math} = $1;
} elsif ($options{args} ne '') {
return 1;
}
if (defined($apply_threshold) && $apply_threshold =~ /^\s*(0|1)\s*$/ ) {
$args->{apply_threshold} = $1;
}
return (0, $args);
}
sub parse_pfdata_eval {
my ($self, %options) = @_;
# --extend-perfdata=perfx,,eval(%(label) =~ s/a/A/g)
my $args = { expr => $options{args} };
$args->{expr} =~ s/%\{(.*?)\}/\$values->{$1}/g;
$args->{expr} =~ s/%\((.*?)\)/\$values->{$1}/g;
return (0, $args);
}
sub parse_group_pfdata {
my ($self, %options) = @_;
$options{args} =~ s/^\s+//;
$options{args} =~ s/\s+$//;
my $args = { pattern_pf => $options{args} };
return $args;
}
sub parse_pfdata_min {
my ($self, %options) = @_;
my $args = $self->parse_group_pfdata(%options);
return (0, $args);
}
sub parse_pfdata_max {
my ($self, %options) = @_;
my $args = $self->parse_group_pfdata(%options);
return (0, $args);
}
sub parse_pfdata_average {
my ($self, %options) = @_;
my $args = $self->parse_group_pfdata(%options);
return (0, $args);
}
sub parse_pfdata_sum {
my ($self, %options) = @_;
my $args = $self->parse_group_pfdata(%options);
return (0, $args);
}
sub apply_pfdata_scale {
my ($self, %options) = @_;
return if (${$options{perf}}->{unit} !~ /^([KMGTPEkmgtpe])?(B|b|bps|Bps|b\/s)$/);
my ($src_quantity, $src_unit) = ($1, $2);
my ($value, $dst_quantity, $dst_unit) = centreon::plugins::misc::scale_bytesbit(
value => ${$options{perf}}->{value},
src_quantity => $src_quantity, src_unit => $src_unit, dst_quantity => $options{args}->{quantity}, dst_unit => $options{args}->{unit}
);
${$options{perf}}->{value} = sprintf('%.2f', $value);
if (defined($dst_unit)) {
${$options{perf}}->{unit} = $dst_quantity . $dst_unit;
} else {
${$options{perf}}->{unit} = $options{args}->{quantity} . $options{args}->{unit};
}
if (defined(${$options{perf}}->{max}) && ${$options{perf}}->{max} ne '') {
($value) = centreon::plugins::misc::scale_bytesbit(value => ${$options{perf}}->{max},
src_quantity => $src_quantity, src_unit => $src_unit,
dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
${$options{perf}}->{max} = sprintf('%.2f', $value);
}
foreach my $threshold ('warning', 'critical') {
next if (${$options{perf}}->{$threshold} eq '');
my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
next if ($status == 0);
if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
($result->{start}) = centreon::plugins::misc::scale_bytesbit(value => $result->{start},
src_quantity => $src_quantity, src_unit => $src_unit,
dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
}
if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
($result->{end}) = centreon::plugins::misc::scale_bytesbit(value => $result->{end},
src_quantity => $src_quantity, src_unit => $src_unit,
dst_quantity => defined($dst_unit) ? $dst_quantity : $options{args}->{quantity},
dst_unit => defined($dst_unit) ? $dst_unit : $options{args}->{unit});
}
${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
}
}
sub apply_pfdata_invert {
my ($self, %options) = @_;
return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq '');
${$options{perf}}->{value} = ${$options{perf}}->{max} - ${$options{perf}}->{value};
foreach my $threshold ('warning', 'critical') {
next if (${$options{perf}}->{$threshold} eq '');
my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
next if ($status == 0);
my $tmp = { arobase => $result->{arobase}, infinite_pos => 0, infinite_neg => 0, start => $result->{start}, end => $result->{end} };
$tmp->{infinite_neg} = 1 if ($result->{infinite_pos} == 1);
$tmp->{infinite_pos} = 1 if ($result->{infinite_neg} == 1);
if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
$tmp->{end} = ${$options{perf}}->{max} - $result->{start};
}
if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
$tmp->{start} = ${$options{perf}}->{max} - $result->{end};
}
${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$tmp);
}
}
sub apply_pfdata_percent {
my ($self, %options) = @_;
return if (!defined(${$options{perf}}->{max}) || ${$options{perf}}->{max} eq '');
${$options{perf}}->{value} = sprintf('%.2f', ${$options{perf}}->{value} * 100 / ${$options{perf}}->{max});
${$options{perf}}->{unit} = '%';
foreach my $threshold ('warning', 'critical') {
next if (${$options{perf}}->{$threshold} eq '');
my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
next if ($status == 0);
if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
$result->{start} = sprintf('%.2f', $result->{start} * 100 / ${$options{perf}}->{max});
}
if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
$result->{end} = sprintf('%.2f', $result->{end} * 100 / ${$options{perf}}->{max});
}
${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
}
${$options{perf}}->{max} = 100;
}
sub apply_pfdata_eval {
my ($self, %options) = @_;
${$options{perf}} = $self->open_eval(eval => $options{args}->{expr}, values => ${$options{perf}});
}
sub apply_pfdata_math {
my ($self, %options) = @_;
my $math = $options{args}->{math};
$math =~ s/current/\$value/g;
my $value = ${$options{perf}}->{value};
eval "\${\$options{perf}}->{value} = $math";
return if ($options{args}->{apply_threshold} == 0);
foreach my $threshold ('warning', 'critical') {
next if (${$options{perf}}->{$threshold} eq '');
my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => ${$options{perf}}->{$threshold});
next if ($status == 0);
if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
$value = $result->{start};
eval "\$result->{start} = $math";
}
if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
$value = $result->{end};
eval "\$result->{end} = $math";
}
${$options{perf}}->{$threshold} = centreon::plugins::misc::get_threshold_litteral(%$result);
}
${$options{perf}}->{max} = 100;
}
sub apply_pfdata_min {
my ($self, %options) = @_;
my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
my $min;
for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
$min = $self->{perfdatas}->[$i]->{value}
if (!defined($min) || $min > $self->{perfdatas}->[$i]->{value});
}
${$options{perf}}->{value} = $min
if (defined($min));
}
sub apply_pfdata_max {
my ($self, %options) = @_;
my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
my $max;
for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
$max = $self->{perfdatas}->[$i]->{value}
if (!defined($max) || $max < $self->{perfdatas}->[$i]->{value});
}
${$options{perf}}->{value} = $max
if (defined($max));
}
sub apply_pfdata_sum {
my ($self, %options) = @_;
my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
my ($sum, $num) = (0, 0);
for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
$sum += $self->{perfdatas}->[$i]->{value};
$num++;
}
${$options{perf}}->{value} = $sum
if ($num > 0);
}
sub apply_pfdata_average {
my ($self, %options) = @_;
my $pattern_pf = $self->assign_eval(eval => "\"$options{args}->{pattern_pf}\"");
my ($sum, $num) = (0, 0);
for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
next if ($self->{perfdatas}->[$i]->{label} !~ /$pattern_pf/);
next if ($self->{perfdatas}->[$i]->{value} !~ /\d+/);
$sum += $self->{perfdatas}->[$i]->{value};
$num++;
}
${$options{perf}}->{value} = sprintf("%.2f", ($sum / $num))
if ($num > 0);
}
sub apply_perfdata_thresholds {
my ($self, %options) = @_;
foreach (('warning', 'critical')) {
next if (!defined($options{$_}));
my @thresholds = split(':', $options{$_}, -1);
for (my $i = 0; $i < scalar(@thresholds); $i++) {
if ($thresholds[$i] =~ /(\d+(?:\.\d+)?)\s*%/) {
if (!defined($options{max}) || $options{max} eq '') {
$thresholds[$i] = '';
next;
}
$thresholds[$i] = $1 * $options{max} / 100;
} elsif ($thresholds[$i] =~ /(\d+(?:\.\d+)?)/) {
$thresholds[$i] = $1;
} else {
$thresholds[$i] = '';
}
}
${$options{perf}}->{$_} = join(':', @thresholds);
}
}
sub load_perfdata_extend_args {
my ($self, %options) = @_;
foreach (
[$self->{option_results}->{change_perfdata}, 1],
[$self->{option_results}->{extend_perfdata}, 2],
[$self->{option_results}->{extend_perfdata_group}, 3],
) {
next if (!defined($_->[0]));
foreach my $arg (@{$_->[0]}) {
$self->parse_perfdata_extend_args(arg => $arg, type => $_->[1]);
}
}
}
sub parse_perfdata_extend_args {
my ($self, %options) = @_;
# --extend-perfdata=searchlabel,newlabel,method[,[newuom],[min],[max],[warning],[critical]]
my ($pfdata_match, $pfdata_substitute, $method, $uom_sub, $min_sub, $max_sub, $warn_sub, $crit_sub) =
split /,/, $options{arg};
return if ((!defined($pfdata_match) || $pfdata_match eq '') && $options{type} != 3);
$self->{pfdata_extends} = [] if (!defined($self->{pfdata_extends}));
my $pfdata_extends = {
pfdata_match => defined($pfdata_match) && $pfdata_match ne '' ? $pfdata_match : undef,
pfdata_substitute => defined($pfdata_substitute) && $pfdata_substitute ne '' ? $pfdata_substitute : undef,
uom_sub => defined($uom_sub) && $uom_sub ne '' ? $uom_sub : undef,
min_sub => defined($min_sub) && $min_sub ne '' ? $min_sub : undef,
max_sub => defined($max_sub) && $max_sub ne '' ? $max_sub : undef,
warn_sub => defined($warn_sub) && $warn_sub ne '' ? $warn_sub : undef,
crit_sub => defined($crit_sub) && $crit_sub ne '' ? $crit_sub : undef,
type => $options{type}
};
if (defined($method) && $method ne '') {
if ($method !~ /^\s*(invert|percent|scale|math|min|max|average|sum|eval)\s*\(\s*(.*?)\s*\)\s*$/) {
$self->output_add(long_msg => "method in argument '$options{arg}' is unknown", debug => 1);
return ;
}
$pfdata_extends->{method_name} = $1;
my $args = $2;
if (my $func = $self->can('parse_pfdata_' . $pfdata_extends->{method_name})) {
(my $status, $pfdata_extends->{method_args}) = $func->($self, args => $args);
if ($status == 1) {
$self->output_add(long_msg => "argument in method '$options{arg}' is unknown", debug => 1);
return ;
}
}
}
push @{$self->{pfdata_extends}}, $pfdata_extends;
}
sub apply_perfdata_explode {
my ($self, %options) = @_;
return if ($self->{explode_perfdata_total} == 0);
foreach (@{$self->{perfdatas}}) {
next if ($_->{max} eq '');
if ($self->{explode_perfdata_total} == 2) {
$self->perfdata_add(label => $_->{label} . '_max', value => $_->{max}, unit => $_->{unit});
next;
}
foreach my $regexp (keys %{$self->{explode_perfdatas}}) {
if ($_->{label} =~ /$regexp/) {
$self->perfdata_add(label => $self->{explode_perfdatas}->{$regexp}, value => $_->{max}, unit => $_->{unit});
last;
}
}
}
}
sub apply_perfdata_extend {
my ($self, %options) = @_;
foreach my $extend (@{$self->{pfdata_extends}}) {
my $new_pfdata = [];
# Manage special case when type group and pfdata_match empty
if ($extend->{type} == 3 && (!defined($extend->{pfdata_match}) || $extend->{pfdata_match} eq '')) {
next if (!defined($extend->{pfdata_substitute}) || $extend->{pfdata_substitute} eq '');
my $new_perf = {
label => $extend->{pfdata_substitute}, value => '',
unit => defined($extend->{uom_sub}) ? $extend->{uom_sub} : '',
warning => '', critical => '',
min => defined($extend->{min_sub}) ? $extend->{min_sub} : '',
max => defined($extend->{max_sub}) ? $extend->{max_sub} : ''
};
if (defined($extend->{method_name})) {
my $func = $self->can('apply_pfdata_' . $extend->{method_name});
$func->($self, perf => \$new_perf, args => $extend->{method_args});
}
$self->apply_perfdata_thresholds(
perf => \$new_perf,
warning => $extend->{warn_sub},
critical => $extend->{crit_sub},
max => $new_perf->{max}
);
if (length($new_perf->{value})) {
push @{$self->{perfdatas}}, $new_perf;
}
next;
}
for (my $i = 0; $i < scalar(@{$self->{perfdatas}}); $i++) {
next if ($self->{perfdatas}->[$i]->{label} !~ /$extend->{pfdata_match}/);
my $new_perf = { %{$self->{perfdatas}->[$i]} };
if ($extend->{type} == 3) {
$new_perf = { label => $self->{perfdatas}->[$i]->{label}, value => '', unit => '', warning => '', critical => '', min => '', max => '' };
}
if (defined($extend->{pfdata_substitute})) {
eval "\$new_perf->{label} =~ s{$extend->{pfdata_match}}{$extend->{pfdata_substitute}}";
}
if (defined($extend->{method_name})) {
my $func = $self->can('apply_pfdata_' . $extend->{method_name});
$func->($self, perf => \$new_perf, args => $extend->{method_args});
}
$new_perf->{unit} = $extend->{uom_sub} if (defined($extend->{uom_sub}));
$new_perf->{min} = $extend->{min_sub} if (defined($extend->{min_sub}));
$new_perf->{max} = $extend->{max_sub} if (defined($extend->{max_sub}));
$self->apply_perfdata_thresholds(
perf => \$new_perf,
warning => $extend->{warn_sub},
critical => $extend->{crit_sub},
max => $new_perf->{max}
);
if ($extend->{type} == 1) {
$self->{perfdatas}->[$i] = $new_perf;
} else {
push @$new_pfdata, $new_perf if (length($new_perf->{value}));
}
}
push @{$self->{perfdatas}}, @$new_pfdata;
}
}
sub change_perfdata {
my ($self, %options) = @_;
$self->apply_perfdata_extend();
$self->apply_perfdata_explode();
}
1;
=head1 NAME
Output class
=head1 SYNOPSIS
-
=head1 OUTPUT OPTIONS
=over 8
=item B<--verbose>
Display extended status information (long output).
=item B<--debug>
Display debug messages.
=item B<--filter-perfdata>
Filter perfdata that match the regexp.
Example: adding --filter-perfdata='avg' will remove all metrics that do not contain
'avg' from performance data.
=item B<--filter-perfdata-adv>
Filter perfdata based on a "if" condition using the following variables:
label, value, unit, warning, critical, min, max.
Variables must be written either %{variable} or %(variable).
Example: adding --filter-perfdata-adv='not (%(value) == 0 and %(max) eq "")' will
remove all metrics whose value equals 0 and that don't have a maximum value.
=item B<--explode-perfdata-max>
Create a new metric for each metric that comes with a maximum limit. The new
metric will be named identically with a '_max' suffix.
Example: it will split 'used_prct'=26.93%;0:80;0:90;0;100
into 'used_prct'=26.93%;0:80;0:90;0;100 'used_prct_max'=100%;;;;
=item B<--change-perfdata> B<--extend-perfdata>
Change or extend perfdata.
Syntax: --extend-perfdata=searchlabel,newlabel,target[,[<new-unit-of-mesure>],[min],[max]]
Common examples:
=over 4
Convert storage free perfdata into used: --change-perfdata='free,used,invert()'
Convert storage free perfdata into used: --change-perfdata='used,free,invert()'
Scale traffic values automatically: --change-perfdata='traffic,,scale(auto)'
Scale traffic values in Mbps: --change-perfdata='traffic_in,,scale(Mbps),mbps'
Change traffic values in percent: --change-perfdata='traffic_in,,percent()'
=back
=item B<--extend-perfdata-group>
Add new aggregated metrics (min, max, average or sum) for groups of metrics defined by a regex match on the metrics' names.
Syntax: --extend-perfdata-group=regex,<names-of-new-metrics>,calculation[,[<new-unit-of-mesure>],[min],[max]]
regex: regular expression
<names-of-new-metrics>: how the new metrics' names are composed (can use $1, $2... for groups defined by () in regex).
calculation: how the values of the new metrics should be calculated
<new-unit-of-mesure> (optional): unit of measure for the new metrics
min (optional): lowest value the metrics can reach
max (optional): highest value the metrics can reach
Common examples:
=over 4
Sum wrong packets from all interfaces (with interface need --units-errors=absolute): --extend-perfdata-group=',packets_wrong,sum(packets_(discard|error)_(in|out))'
Sum traffic by interface: --extend-perfdata-group='traffic_in_(.*),traffic_$1,sum(traffic_(in|out)_$1)'
=back
=item B<--change-short-output> B<--change-long-output>
Modify the short/long output that is returned by the plugin.
Syntax: --change-short-output=pattern~replacement~modifier
Most commonly used modifiers are i (case insensitive) and g (replace all occurrences).
Example: adding --change-short-output='OK~Up~gi' will replace all occurrences of 'OK', 'ok', 'Ok' or 'oK' with 'Up'
=item B<--change-exit>
Replace an exit code with one of your choice.
Example: adding --change-exit=unknown=critical will result in a CRITICAL state
instead of an UNKNOWN state.
=item B<--change-output-adv>
Replace short output and exit code based on a "if" condition using the following variables:
short_output, exit_code.
Variables must be written either %{variable} or %(variable).
Example: adding --change-output-adv='%(short_ouput) =~ /UNKNOWN: No daemon/,OK: No daemon,OK' will
change the following specific UNKNOWN result to an OK result.
=item B<--range-perfdata>
Rewrite the ranges displayed in the perfdata. Accepted values:
0: nothing is changed.
1: if the lower value of the range is equal to 0, it is removed.
2: remove the thresholds from the perfdata.
=item B<--filter-uom>
Mask the units when they don't match the given regular expression.
=item B<--opt-exit>
Replace the exit code in case of an execution error (i.e. wrong option provided,
SSH connection refused, timeout, etc). Default: unknown.
=item B<--output-ignore-perfdata>
Remove all the metrics from the service. The service will still have a status
and an output.
=item B<--output-ignore-label>
Remove the status label ("OK:", "WARNING:", "UNKNOWN:", CRITICAL:") from the
beginning of the output.
Example: 'OK: Ram Total:...' will become 'Ram Total:...'
=item B<--output-xml>
Return the output in XML format (to send to an XML API).
=item B<--output-json>
Return the output in JSON format (to send to a JSON API).
=item B<--output-openmetrics>
Return the output in OpenMetrics format (to send to a tool expecting this
format).
=item B<--output-file>
Write output in file (can be combined with JSON, XML and OpenMetrics options).
Example: --output-file=/tmp/output.txt will write the output in /tmp/output.txt.
=item B<--disco-format>
Applies only to modes beginning with 'list-'.
Returns the list of available macros to configure a service discovery rule
(formatted in XML).
=item B<--disco-show>
Applies only to modes beginning with 'list-'.
Returns the list of discovered objects (formatted in XML) for service discovery.
=item B<--float-precision>
Define the float precision for thresholds (default: 8).
=item B<--source-encoding>
Define the character encoding of the response sent by the monitored resource
Default: 'UTF-8'.
=head1 DESCRIPTION
B<output>.
=cut
CENTREON_PLUGINS_OUTPUT
$fatpacked{"centreon/plugins/passwordmgr/centreonvault.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_CENTREONVAULT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::centreonvault;
use strict;
use warnings;
use Data::Dumper;
use centreon::plugins::http;
use JSON::XS;
use MIME::Base64;
use Crypt::OpenSSL::AES;
use centreon::plugins::statefile;
my $VAULT_PATH_REGEX = qr/^secret::hashicorp_vault::([^:]+)::(.+)$/;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr needs an 'output' argument that must be of type centreon::plugins::output.\n";
exit 3;
}
if (!defined($options{options})) {
print "Class PasswordMgr needs an 'options' argument that must be of type centreon::plugins::options.\n";
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
'vault-config:s' => { name => 'vault_config', default => '/etc/centreon-engine/centreonvault.json'},
'vault-cache:s' => { name => 'vault_cache', default => '/var/lib/centreon/centplugins/centreonvault_session'},
'vault-env-file:s' => { name => 'vault_env_file', default => '/usr/share/centreon/.env'},
});
$options{options}->add_help(package => __PACKAGE__, sections => 'VAULT OPTIONS');
$self->{output} = $options{output};
# to access the vault, http protocol management is needed
$self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl', insecure => 1);
# to store the token and its expiration date, a statefile is needed
$self->{cache} = centreon::plugins::statefile->new();
return $self;
}
sub extract_map_options {
my ($self, %options) = @_;
$self->{map_option} = [];
# Parse all options to find '/# .*\:\:secret\:\:(.*)/' pattern in the options values and add entries in map_option
foreach my $option (keys %{$options{option_results}}) {
if (defined($options{option_results}{$option})) {
next if ($option eq 'map_option');
if (ref($options{option_results}{$option}) eq 'ARRAY') {
foreach (@{$options{option_results}{$option}}) {
if ($_ =~ $VAULT_PATH_REGEX) {
push (@{$self->{request_endpoint}}, "/v1/$1::$2");
push (@{$self->{map_option}}, $option."=%".$_);
}
}
} else {
if (my ($path, $secret) = $options{option_results}{$option} =~ $VAULT_PATH_REGEX) {
push (@{$self->{request_endpoint}}, "/v1/" . $path . "::" . $secret);
push (@{$self->{map_option}}, $option."=%".$options{option_results}{$option});
}
}
}
}
}
sub vault_settings {
my ($self, %options) = @_;
if (centreon::plugins::misc::is_empty($options{option_results}->{vault_config})) {
$self->{output}->add_option_msg(short_msg => "Please provide a Centreon Vault configuration file path with --vault-config option");
$self->{output}->option_exit();
}
if (! -f $options{option_results}->{vault_config}) {
$self->{output}->add_option_msg(short_msg => "File '$options{option_results}->{vault_config}' could not be found.");
$self->{output}->option_exit();
}
$self->{vault_cache} = $options{option_results}->{vault_cache};
$self->{vault_env_file} = $options{option_results}->{vault_env_file};
$self->{vault_config} = $options{option_results}->{vault_config};
my $file_content = do {
local $/ = undef;
if (!open my $fh, "<", $options{option_results}->{vault_config}) {
$self->{output}->add_option_msg(short_msg => "Could not read file $options{option_results}->{vault_config}: $!");
$self->{output}->option_exit();
}
<$fh>;
};
# decode the JSON content of the file
my $json = centreon::plugins::misc::json_decode($file_content);
if (!defined($json)) {
$self->{output}->add_option_msg(short_msg => "Cannot decode JSON : $file_content\n");
$self->{output}->option_exit();
}
# set the default values
$self->{vault}->{protocol} = 'https';
$self->{vault}->{url} = '127.0.0.1';
$self->{vault}->{port} = '8100';
# define the list of expected attributes in the JSON file
my @valid_json_options = (
'protocol',
'url',
'port',
'root_path',
'token',
'secret_id',
'role_id'
);
# set the object fields when the json fields are not empty
foreach my $valid_option (@valid_json_options) {
$self->{vault}->{$valid_option} = $json->{$valid_option}
if ( !centreon::plugins::misc::is_empty( $json->{ $valid_option } ) );
}
return 1;
}
sub get_decryption_key {
my ($self, %options) = @_;
# try getting APP_SECRET from the environment variables
if ( !centreon::plugins::misc::is_empty($ENV{'APP_SECRET'}) ) {
return $ENV{'APP_SECRET'};
}
# try getting APP_SECRET defined in the env file (default: /usr/share/centreon/.env) file
my $fh;
return undef if (!open $fh, "<", $self->{vault_env_file});
for my $line (<$fh>) {
if ($line =~ /^APP_SECRET=(.*)$/) {
return $1;
}
}
return undef;
}
sub extract_and_decrypt {
my ($self, %options) = @_;
my $input = decode_base64($options{data});
my $key = $options{key};
# with AES-256, the IV length must 16 bytes
my $iv_length = 16;
# extract the IV, the hashed data, the encrypted data
my $iv = substr($input, 0, $iv_length); # initialization vector
my $hashed_data = substr($input, $iv_length, 64); # hmac of the original data, for integrity control
my $encrypted_data = substr($input, $iv_length + 64); # data to decrypt
# Creating the AES decryption object
my $cipher;
eval {
$cipher = Crypt::OpenSSL::AES->new(
$key,
{
'cipher' => 'AES-256-CBC',
'iv' => $iv
}
);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "There was an error while creating the AES object: " . $@);
$self->{output}->option_exit();
}
# Decrypting the data
my $decrypted_data;
eval {$decrypted_data = $cipher->decrypt($encrypted_data);};
if ($@) {
$self->{output}->add_option_msg(short_msg => "There was an error while decrypting an AES-encrypted data: " . $@);
$self->{output}->option_exit();
}
return $decrypted_data;
}
sub is_token_still_valid {
my ($self) = @_;
if (
!defined($self->{auth})
|| centreon::plugins::misc::is_empty($self->{auth}->{token})
|| centreon::plugins::misc::is_empty($self->{auth}->{expiration_epoch})
|| $self->{auth}->{expiration_epoch} !~ /\d+/
|| $self->{auth}->{expiration_epoch} <= time()
) {
$self->{output}->output_add(long_msg => "The token is missing or has expired or is invalid.", debug => 1);
return undef;
}
$self->{output}->output_add(long_msg => "The cached token is still valid.", debug => 1);
# Possible enhancement: check the token validity by calling this endpoint: /v1/auth/token/lookup-self
# {"request_id":"XXXXX","lease_id":"","renewable":false,"lease_duration":0,"data":{"accessor":"XXXXXXX","creation_time":1732294406,"creation_ttl":2764800,"display_name":"approle","entity_id":"XXX","expire_time":"2024-12-24T16:53:26.932122122Z","explicit_max_ttl":0,"id":"hvs.secretToken","issue_time":"2024-11-22T16:53:26.932129132Z","meta":{"role_name":"myvault"},"num_uses":0,"orphan":true,"path":"auth/approle/login","policies":["default","myvault"],"renewable":true,"ttl":2764724,"type":"service"},"wrap_info":null,"warnings":null,"auth":null,"mount_type":"token"}
return 1;
}
sub check_authentication {
my ($self, %options) = @_;
# prepare the cache (aka statefile)
$self->{cache}->check_options(option_results => $options{option_results});
my ($dir, $file, $suffix) = $options{option_results}->{vault_cache} =~ /^(.*\/)([^\/]+)(_.*)?$/;
# Try reading the cache file
if ($self->{cache}->read(
statefile => $file,
statefile_suffix => defined($suffix) ? $suffix : '',
statefile_dir => $dir,
statefile_format => 'json'
)) {
# if the cache file could be read, get the token and its expiration date
$self->{auth} = {
token => $self->{cache}->get(name => 'token'),
expiration_epoch => $self->{cache}->get(name => 'expiration_epoch')
};
}
# if it is not valid, authenticate to get a new token
if ( !$self->is_token_still_valid() ) {
return $self->authenticate();
}
return 1;
}
sub authenticate {
my ($self) = @_;
# initial value: assuming the role and secret id might not be encrypted
my $role_id = $self->{vault}->{role_id};
my $secret_id = $self->{vault}->{secret_id};
if (centreon::plugins::misc::is_empty($role_id) || centreon::plugins::misc::is_empty($secret_id)) {
$self->{output}->add_option_msg(short_msg => "Unable to authenticate to the vault: role_id or secret_id is empty.");
$self->{output}->option_exit();
}
my $decryption_key = $self->get_decryption_key();
# Decrypt the role_id and the secret_id if we have a decryption key
if ( !centreon::plugins::misc::is_empty($decryption_key) ) {
$role_id = $self->extract_and_decrypt(
data => $role_id,
key => $decryption_key
);
$secret_id = $self->extract_and_decrypt(
data => $secret_id,
key => $decryption_key
);
}
# Authenticate to get the token
my ($auth_result_json) = $self->{http}->request(
hostname => $self->{vault}->{url},
port => $self->{vault}->{port},
proto => $self->{vault}->{protocol},
method => 'POST',
url_path => "/v1/auth/approle/login",
query_form_post => "role_id=$role_id&secret_id=$secret_id",
header => [
'Content-Type: application/x-www-form-urlencoded',
'Accept: */*',
'X-Vault-Request: true',
'User-Agent: Centreon-Plugins'
]
);
# Convert the response into a JSON object
my $auth_result_obj = centreon::plugins::misc::json_decode($auth_result_json);
if (!defined($auth_result_obj)) {
# exit with UNKNOWN status
$self->{output}->add_option_msg(short_msg => "Cannot decode JSON response from the vault server: $auth_result_json.");
$self->{output}->option_exit();
}
# Authentication to the vault has passed
# store the token (.auth.client_token) and its expiration date (current date + .lease_duration)
my $expiration_epoch = -1;
my $lease_duration = $auth_result_obj->{auth}->{lease_duration};
if ( defined($lease_duration)
&& $lease_duration =~ /\d+/
&& $lease_duration > 0 ) {
$expiration_epoch = time() + $lease_duration;
}
$self->{auth} = {
'token' => $auth_result_obj->{auth}->{client_token},
'expiration_epoch' => $expiration_epoch
};
$self->{cache}->write(data => $self->{auth}, name => 'auth');
$self->{output}->output_add(long_msg => "Authenticating worked. Token valid until "
. localtime($self->{auth}->{expiration_epoch}), debug => 1);
return 1;
}
sub request_api {
my ($self, %options) = @_;
$self->vault_settings(%options);
# check the authentication
if (!$self->check_authentication(%options)) {
$self->{output}->add_option_msg(short_msg => "Unable to authenticate to the vault.");
$self->{output}->option_exit();
}
$self->{lookup_values} = {};
foreach my $item (@{$self->{request_endpoint}}) {
# Extract vault name configuration from endpoint
# 'vault::/v1/<root_path>/monitoring/hosts/7ad55afc-fa9e-4851-85b7-e26f47e421d7'
my ($endpoint, $secret) = $item =~ /^(.*)\:\:(.*)$/;
my ($response) = $self->{http}->request(
hostname => $self->{vault}->{url},
port => $self->{vault}->{port},
proto => $self->{vault}->{protocol},
method => 'GET',
url_path => $endpoint,
header => [
'Accept: application/json',
'User-Agent: Centreon-Plugins',
'X-Vault-Request: true',
'X-Vault-Token: ' . $self->{auth}->{token}
]
);
my $json = centreon::plugins::misc::json_decode($response);
if (!defined($json->{data})) {
$self->{output}->add_option_msg(short_msg => "Cannot decode Vault JSON response: $response");
$self->{output}->option_exit();
};
foreach my $secret_name (keys %{$json->{data}->{data}}) {
# e.g. secret::hashicorp_vault::myspace/data/snmp::PubCommunity
$self->{lookup_values}->{'secret::hashicorp_vault::' . substr($endpoint, index($endpoint, '/', 1) + 1) . '::' . $secret_name} = $json->{data}->{data}->{$secret_name};
}
}
}
sub do_map {
my ($self, %options) = @_;
foreach my $mapping (@{$self->{map_option}}) {
my ($opt_name, $opt_value) = $mapping =~ /^(.+?)=%(.+)$/ or next;
$opt_name =~ s/-/_/g;
$options{option_results}->{$opt_name} = defined($self->{lookup_values}->{$opt_value}) ? $self->{lookup_values}->{$opt_value} : $opt_value;
}
}
sub manage_options {
my ($self, %options) = @_;
$self->extract_map_options(%options);
return if (scalar(@{$self->{map_option}}) <= 0);
$self->request_api(%options);
$self->do_map(%options);
}
1;
=head1 NAME
Centreon Vault password manager
=head1 SYNOPSIS
Centreon Vault password manager
To be used with an array containing keys/values saved in a secret path by resource
=head1 VAULT OPTIONS
=over 8
=item B<--vault-config>
Path to the file defining access to the Centreon vault (default: C</etc/centreon-engine/centreonvault.json>).
=item B<--vault-cache>
Path to the file where the token to access the Centreon vault will be stored (default: C</var/lib/centreon/centplugins/centreonvault_session>).
=item B<--vault-env-file>
Path to the file containing the APP_SECRET variable (default: C</usr/share/centreon/.env>).
=back
=head1 DESCRIPTION
B<centreonvault>.
=cut
=head1 NAME
centreon::plugins::passwordmgr::centreonvault - Module for getting secrets from Centreon Vault.
=head1 SYNOPSIS
use centreon::plugins::passwordmgr::centreonvault;
my $vault = centreon::plugins::passwordmgr::centreonvault->new(output => $output, options => $options);
$vault->manage_options(option_results => \%option_results);
=head1 DESCRIPTION
This module provides methods to retrieve secrets (passwords, SNMP communities, ...) from Centreon Vault (adequately
configured HashiCorp Vault).
It extracts and decrypt the information required to login to the vault from the vault configuration file, authenticates
to the vault, retrieves secrets, and maps them to the corresponding options for the centreon-plugins to work with.
=head1 METHODS
=head2 new
my $vault = centreon::plugins::passwordmgr::centreonvault->new(%options);
Creates a new `centreon::plugins::passwordmgr::centreonvault` object. The `%options` hash can include:
=over 4
=item * output
The output object for displaying debug and error messages.
=item * options
The options object for handling command-line options.
=back
=head2 extract_map_options
$vault->extract_map_options(option_results => \%option_results);
Extracts and maps options that match the Vault path regex pattern (C</^secret::hashicorp_vault::([^:]+)::(.+)$/>). The
`%option_results` hash should include the command-line options.
=head2 vault_settings
$vault->vault_settings(option_results => \%option_results);
Loads and validates the Vault configuration from the specified file.
The `%option_results` hash should include the command-line options.
=head2 get_decryption_key
my $key = $vault->get_decryption_key();
Retrieves the decryption key from C<APP_SECRET> environment variable. It will look for it in the the specified
environment file if it is not available in the environment variables.
=head2 extract_and_decrypt
my $decrypted_data = $vault->extract_and_decrypt(data => $data, key => $key);
Decrypts the given data using the specified key. The options must include:
=over 4
=item * data
The base64-encoded data to decrypt.
=item * key
The base64-encoded decryption key.
=back
=head2 is_token_still_valid
my $is_valid = $vault->is_token_still_valid();
Checks if there is a token in the cache and if it is still valid based on its expiration date. Returns 1 if valid, otherwise undef.
=head2 check_authentication
$vault->check_authentication(option_results => \%option_results);
Checks the authentication status and retrieves a new token if necessary. The `%option_results` hash should include the command-line options.
=head2 authenticate
$vault->authenticate();
Authenticates to the Vault, retrieves a new token and stores it in the dedicated cache file.
=head2 request_api
$vault->request_api(option_results => \%option_results);
Sends requests to the Vault API to retrieve secrets. The `%option_results` hash should include the command-line options.
=head2 do_map
$vault->do_map(option_results => \%option_results);
Maps the retrieved secrets to the corresponding options. The `%option_results` hash should include the command-line options.
Calling this method will update the `%option_results` hash replacing vault paths with the retrieved secrets.
=head2 manage_options
$vault->manage_options(option_results => \%option_results);
Manages the options by extracting, requesting, and mapping secrets. The `%option_results` hash should include the command-line options.
NB: This is the main method to be called from outside the module. All other methods are intended to be used internally.
=head1 AUTHOR
Centreon
=head1 LICENSE
Licensed under the Apache License, Version 2.0.
=cut
CENTREON_PLUGINS_PASSWORDMGR_CENTREONVAULT
$fatpacked{"centreon/plugins/passwordmgr/environment.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_ENVIRONMENT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::environment;
use strict;
use warnings;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
'environment-map-option:s@' => { name => 'environment_map_option' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'ENVIRONMENT OPTIONS');
$self->{output} = $options{output};
return $self;
}
sub manage_options {
my ($self, %options) = @_;
return if (!defined($options{option_results}->{environment_map_option}));
foreach (@{$options{option_results}->{environment_map_option}}) {
next if (! /^(.+?)=(.+)$/);
my ($option, $map) = ($1, $2);
$option =~ s/-/_/g;
$options{option_results}->{$option} = defined($ENV{$map}) ? $ENV{$map} : '';
}
}
1;
=head1 NAME
Environment global
=head1 SYNOPSIS
environment class
=head1 ENVIRONMENT OPTIONS
=over 8
=item B<--environment-map-option>
Overload plugin option.
Example:
--environment-map-option="snmp-community=SNMPCOMMUNITY"
=back
=head1 DESCRIPTION
B<environment>.
=cut
CENTREON_PLUGINS_PASSWORDMGR_ENVIRONMENT
$fatpacked{"centreon/plugins/passwordmgr/file.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_FILE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::file;
use strict;
use warnings;
use centreon::plugins::misc;
use JSON::Path;
use JSON::XS;
use Data::Dumper;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
'secret-file:s' => { name => 'secret_file' },
'secret-search-value:s@' => { name => 'secret_search_value' },
'secret-map-option:s@' => { name => 'secret_map_option' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'SECRET FILE OPTIONS');
$self->{output} = $options{output};
$JSON::Path::Safe = 0;
return $self;
}
sub load {
my ($self, %options) = @_;
if (defined($options{option_results}->{secret_file}) && $options{option_results}->{secret_file} ne '') {
if (! -f $options{option_results}->{secret_file} or ! -r $options{option_results}->{secret_file}) {
$self->{output}->add_option_msg(short_msg => "Cannot read secret file '$options{option_results}->{secret_file}': $!");
$self->{output}->option_exit();
}
my $content = centreon::plugins::misc::slurp_file(output => $self->{output}, file => $options{option_results}->{secret_file});
my $decoded;
eval {
$decoded = JSON::XS->new->utf8->decode($content);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode secret file");
$self->{output}->option_exit();
}
return $decoded;
}
}
sub do_lookup {
my ($self, %options) = @_;
$self->{lookup_values} = {};
return if (!defined($options{option_results}->{secret_search_value}));
foreach (@{$options{option_results}->{secret_search_value}}) {
next if (! /^(.+?)=(.+)$/);
my ($map, $lookup) = ($1, $2);
# Change %{xxx} options usage
while ($lookup =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
$lookup =~ s/\%\{$1\}/$sub/g
}
my $jpath = JSON::Path->new($lookup);
my $result = $jpath->value($options{json});
$self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
$self->{lookup_values}->{$map} = $result;
}
}
sub do_map {
my ($self, %options) = @_;
return if (!defined($options{option_results}->{secret_map_option}));
foreach (@{$options{option_results}->{secret_map_option}}) {
next if (! /^(.+?)=(.+)$/);
my ($option, $map) = ($1, $2);
# Change %{xxx} options usage
while ($map =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
$map =~ s/\%\{$1\}/$sub/g
}
$option =~ s/-/_/g;
$options{option_results}->{$option} = $map;
}
}
sub manage_options {
my ($self, %options) = @_;
my $secrets = $self->load(%options);
return if (!defined($secrets));
$self->do_lookup(%options, json => $secrets);
$self->do_map(%options);
}
1;
=head1 NAME
Secret file global
=head1 SYNOPSIS
secret file class
=head1 SECRET FILE OPTIONS
=over 8
=item B<--secret-file>
Secret file.
=item B<--secret-search-value>
Looking for a value in the JSON. Can use JSON Path and other option values.
Example:
--secret-search-value='password=$..entries.[?($_->{title} =~ /server/i)].password'
--secret-search-value='username=$..entries.[?($_->{title} =~ /server/i)].username'
--secret-search-value='password=$..entries.[?($_->{title} =~ /%{hostname}/i)].password'
=item B<--secret-map-option>
Overload plugin option.
Example:
--secret-map-option="password=%{password}"
--secret-map-option="username=%{username}"
=back
=head1 DESCRIPTION
B<secret file>.
=cut
CENTREON_PLUGINS_PASSWORDMGR_FILE
$fatpacked{"centreon/plugins/passwordmgr/hashicorpvault.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_HASHICORPVAULT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::hashicorpvault;
use strict;
use warnings;
use Data::Dumper;
use centreon::plugins::http;
use Digest::MD5 qw(md5_hex);
use JSON::XS;
use vars qw($vault_connections);
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
'auth-method:s' => { name => 'auth_method', default => 'token' },
'auth-path:s' => { name => 'auth_path' },
'auth-settings:s%' => { name => 'auth_settings' },
'map-option:s@' => { name => 'map_option' },
'secret-path:s@' => { name => 'secret_path' },
'vault-address:s' => { name => 'vault_address'},
'vault-port:s' => { name => 'vault_port', default => '8200' },
'vault-protocol:s' => { name => 'vault_protocol', default => 'http'},
'vault-token:s' => { name => 'vault_token'}
});
$options{options}->add_help(package => __PACKAGE__, sections => 'VAULT OPTIONS');
$self->{output} = $options{output};
$self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl');
return $self;
}
sub get_access_token {
my ($self, %options) = @_;
my $decoded;
my $login = $self->parse_auth_method(method => $self->{auth_method}, settings => $self->{auth_settings});
my $post_json = JSON::XS->new->utf8->encode($login);
if (!defined($self->{auth_path}) || $self->{auth_path} eq '') {
$self->{auth_path} = $self->{auth_method};
}
my $url_path = '/v1/auth/'. $self->{auth_path} . '/login/';
$url_path .= $self->{auth_settings}->{username} if (defined($self->{auth_settings}->{username}) && $self->{auth_method} =~ 'userpass|login') ;
my $content = $self->{http}->request(
hostname => $self->{vault_address},
port => $self->{vault_port},
proto => $self->{vault_protocol},
method => 'POST',
header => ['Content-type: application/json'],
query_form_post => $post_json,
url_path => $url_path
);
if (!defined($content) || $content eq '') {
$self->{output}->add_option_msg(short_msg => "Authentication endpoint returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']");
$self->{output}->option_exit();
}
eval {
$decoded = JSON::XS->new->utf8->decode($content);
};
if ($@) {
$self->{output}->output_add(long_msg => $content, debug => 1);
$self->{output}->add_option_msg(short_msg => "Cannot decode response (add --debug option to display returned content)");
$self->{output}->option_exit();
}
if (defined($decoded->{errors}[0])) {
$self->{output}->output_add(long_msg => "Error message : " . $decoded->{errors}[0], debug => 1);
$self->{output}->add_option_msg(short_msg => "Authentication endpoint returns error code '" . $decoded->{errors}[0] . "' (add --debug option for detailed message)");
$self->{output}->option_exit();
}
my $access_token = $decoded->{auth}->{client_token};
return $access_token;
}
sub parse_auth_method {
my ($self, %options) = @_;
my $login_settings;
my $settings_mapping = {
azure => [ 'role', 'jwt' ],
cert => [ 'name' ],
github => [ 'token' ],
ldap => [ 'username', 'password' ],
okta => [ 'username', 'password', 'totp' ],
radius => [ 'username', 'password' ],
userpass => [ 'username', 'password' ]
};
foreach (@{$settings_mapping->{$options{method}}}) {
if (!defined($options{settings}->{$_})) {
$self->{output}->add_option_msg(short_msg => 'Missing authentication setting: ' . $_);
$self->{output}->option_exit();
}
$login_settings->{$_} = $options{settings}->{$_};
};
return $login_settings;
}
sub settings {
my ($self, %options) = @_;
if (!defined($options{option_results}->{vault_address}) || $options{option_results}->{vault_address} eq '') {
$self->{output}->add_option_msg(short_msg => "Please set the --vault-address option");
$self->{output}->option_exit();
}
if ($options{option_results}->{auth_method} eq 'token' && (!defined($options{option_results}->{vault_token}) || $options{option_results}->{vault_token} eq '')) {
$self->{output}->add_option_msg(short_msg => "Please set the --vault-token option");
$self->{output}->option_exit();
}
if (!defined($options{option_results}->{secret_path}) || $options{option_results}->{secret_path} eq '') {
$self->{output}->add_option_msg(short_msg => "Please set the --secret-path option");
$self->{output}->option_exit();
}
if (defined($options{option_results}->{auth_path})) {
$self->{auth_path} = lc($options{option_results}->{auth_path});
}
$self->{auth_method} = lc($options{option_results}->{auth_method});
$self->{auth_settings} = defined($options{option_results}->{auth_settings}) && $options{option_results}->{auth_settings} ne '' ? $options{option_results}->{auth_settings} : {};
$self->{vault_address} = $options{option_results}->{vault_address};
$self->{vault_port} = $options{option_results}->{vault_port};
$self->{vault_protocol} = $options{option_results}->{vault_protocol};
$self->{vault_token} = $options{option_results}->{vault_token};
if (lc($self->{auth_method}) !~ m/azure|cert|github|ldap|okta|radius|userpass|token/ ) {
$self->{output}->add_option_msg(short_msg => "Incorrect or unsupported authentication method set in --auth-method");
$self->{output}->option_exit();
}
foreach (@{$options{option_results}->{secret_path}}) {
$self->{request_endpoint}->{$_} = '/v1/' . $_;
}
if (defined($options{option_results}->{auth_method}) && $options{option_results}->{auth_method} ne 'token') {
$self->{vault_token} = $self->get_access_token(%options);
};
$self->{http}->add_header(key => 'Accept', value => 'application/json');
if (defined($self->{vault_token})) {
$self->{http}->add_header(key => 'X-Vault-Token', value => $self->{vault_token});
}
}
sub request_api {
my ($self, %options) = @_;
$self->settings(%options);
my ($raw_data, $raw_response);
foreach my $endpoint (keys %{$self->{request_endpoint}}) {
my $json;
my $response = $self->{http}->request(
hostname => $self->{vault_address},
port => $self->{vault_port},
proto => $self->{vault_protocol},
method => 'GET',
url_path => $self->{request_endpoint}->{$endpoint}
);
$self->{output}->output_add(long_msg => $response, debug => 1);
eval {
$json = JSON::XS->new->utf8->decode($response);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode Vault JSON response: $@");
$self->{output}->option_exit();
}
if ((defined($json->{data}->{metadata}->{deletion_time}) && $json->{data}->{metadata}->{deletion_time} ne '') || $json->{data}->{metadata}->{destroyed} eq 'true') {
$self->{output}->add_option_msg(short_msg => "This secret is not valid anymore");
$self->{output}->option_exit();
}
foreach (keys %{$json->{data}->{data}}) {
$self->{lookup_values}->{'key_' . $endpoint} = $_;
$self->{lookup_values}->{'value_' . $endpoint} = $json->{data}->{data}->{$_};
}
push(@{$raw_data}, $json);
push(@{$raw_response}, $response);
}
return ($raw_data, $raw_response);
}
sub do_map {
my ($self, %options) = @_;
return if (!defined($options{option_results}->{map_option}));
foreach (@{$options{option_results}->{map_option}}) {
next if (! /^(.+?)=(.+)$/);
my ($option, $map) = ($1, $2);
# Change %{xxx} options usage
while ($map =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
$map =~ s/\%\{$1\}/$sub/g;
}
$option =~ s/-/_/g;
$options{option_results}->{$option} = $map;
}
}
sub manage_options {
my ($self, %options) = @_;
my ($content, $debug) = $self->request_api(%options);
if (!defined($content)) {
$self->{output}->add_option_msg(short_msg => "Cannot read Vault information");
$self->{output}->option_exit();
}
$self->do_map(%options);
$self->{output}->output_add(long_msg => Data::Dumper::Dumper($debug), debug => 1) if ($self->{output}->is_debug());
}
1;
=head1 NAME
HashiCorp Vault global
=head1 SYNOPSIS
HashiCorp Vault class
To be used with K/V engines
=head1 VAULT OPTIONS
=over 8
=item B<--vault-address>
IP address of the HashiCorp Vault server (mandatory).
=item B<--vault-port>
Port of the HashiCorp Vault server (default: '8200').
=item B<--vault-protocol>
HTTP of the HashiCorp Vault server.
Can be: 'http', 'https' (default: http).
=item B<--auth-method>
Authentication method to log in against the Vault server.
Can be: 'azure', 'cert', 'github', 'ldap', 'okta', 'radius', 'userpass' (default: 'token');
=item B<--auth-path>
Authentication path for 'userpass'. Is an optional setting.
More information here: https://developer.hashicorp.com/vault/docs/auth/userpass#configuration
=item B<--vault-token>
Directly specify a valid token to log in (only for --auth-method='token').
=item B<--auth-settings>
Required information to log in according to the selected method.
Examples:
for 'userpass': --auth-settings='username=user1' --auth-settings='password=my_password'
for 'azure': --auth-settings='role=my_azure_role' --auth-settings='jwt=my_azure_token'
More information here: https://www.vaultproject.io/api-docs/auth
=item B<--secret-path>
Location of the secret in the Vault K/V engine (mandatory - Can be multiple).
Examples:
for v1 engine: --secret-path='mysecrets/servicecredentials'
for v2 engine: --secret-path='mysecrets/data/servicecredentials?version=12'
More information here: https://www.vaultproject.io/api-docs/secret/kv
=item B<--map-option>
Overload Plugin option with K/V values.
Use the following syntax:
the_option_to_overload='%{key_$secret_path$}' or
the_option_to_overload='%{value_$secret_path$}'
Example:
--map-option='username=%{key_mysecrets/servicecredentials}'
--map-option='password=%{value_mysecrets/servicecredentials}'
=back
=head1 DESCRIPTION
B<hashicorpvault>.
=cut
CENTREON_PLUGINS_PASSWORDMGR_HASHICORPVAULT
$fatpacked{"centreon/plugins/passwordmgr/keepass.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_KEEPASS';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::keepass;
use strict;
use warnings;
use JSON::Path;
use Data::Dumper;
use KeePass::Reader;
use vars qw($keepass_connections);
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
'keepass-endpoint:s' => { name => 'keepass_endpoint' },
'keepass-endpoint-file:s' => { name => 'keepass_endpoint_file' },
'keepass-file:s' => { name => 'keepass_file' },
'keepass-password:s' => { name => 'keepass_password' },
'keepass-search-value:s@' => { name => 'keepass_search_value' },
'keepass-map-option:s@' => { name => 'keepass_map_option' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'KEEPASS OPTIONS');
$self->{output} = $options{output};
$JSON::Path::Safe = 0;
return $self;
}
sub build_api_args {
my ($self, %options) = @_;
$self->{connection_info} = { file => undef, password => undef };
if (defined($options{option_results}->{keepass_endpoint_file}) && $options{option_results}->{keepass_endpoint_file} ne '') {
if (! -f $options{option_results}->{keepass_endpoint_file} or ! -r $options{option_results}->{keepass_endpoint_file}) {
$self->{output}->add_option_msg(short_msg => "Cannot read keepass endpoint file: $!");
$self->{output}->option_exit();
}
require $options{option_results}->{keepass_endpoint_file};
if (defined($keepass_connections) && defined($options{option_results}->{keepass_endpoint}) && $options{option_results}->{keepass_endpoint} ne '') {
if (!defined($keepass_connections->{$options{option_results}->{keepass_endpoint}})) {
$self->{output}->add_option_msg(short_msg => "Endpoint $options{option_results}->{keepass_endpoint} doesn't exist in keepass endpoint file");
$self->{output}->option_exit();
}
$self->{connection_info} = $keepass_connections->{$options{option_results}->{keepass_endpoint}};
}
}
foreach (['keepass_file', 'file'], ['keepass_password', 'password']) {
if (defined($options{option_results}->{$_->[0]}) && $options{option_results}->{$_->[0]} ne '') {
$self->{connection_info}->{$_->[1]} = $options{option_results}->{$_->[0]};
}
}
if (defined($self->{connection_info}->{file}) && $self->{connection_info}->{file} ne '') {
if (!defined($self->{connection_info}->{password}) || $self->{connection_info}->{password} eq '') {
$self->{output}->add_option_msg(short_msg => "Please set keepass-password option");
$self->{output}->option_exit();
}
}
}
sub do_lookup {
my ($self, %options) = @_;
$self->{lookup_values} = {};
return if (!defined($options{option_results}->{keepass_search_value}));
foreach (@{$options{option_results}->{keepass_search_value}}) {
next if (! /^(.+?)=(.+)$/);
my ($map, $lookup) = ($1, $2);
# Change %{xxx} options usage
while ($lookup =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
$lookup =~ s/\%\{$1\}/$sub/g
}
my $jpath = JSON::Path->new($lookup);
my $result = $jpath->value($options{json});
$self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
$self->{lookup_values}->{$map} = $result;
}
}
sub do_map {
my ($self, %options) = @_;
return if (!defined($options{option_results}->{keepass_map_option}));
foreach (@{$options{option_results}->{keepass_map_option}}) {
next if (! /^(.+?)=(.+)$/);
my ($option, $map) = ($1, $2);
# Change %{xxx} options usage
while ($map =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
$map =~ s/\%\{$1\}/$sub/g
}
$option =~ s/-/_/g;
$options{option_results}->{$option} = $map;
}
}
sub manage_options {
my ($self, %options) = @_;
$self->build_api_args(%options);
return if (!defined($self->{connection_info}->{file}));
my $keepass = KeePass::Reader->new();
my $content = $keepass->load_db(file => $self->{connection_info}->{file}, password => $self->{connection_info}->{password});
if (!defined($content)) {
$self->{output}->add_option_msg(short_msg => "Cannot read keepass file: " . $keepass->error());
$self->{output}->option_exit();
}
$self->{output}->output_add(long_msg => Data::Dumper::Dumper($content), debug => 1) if ($self->{output}->is_debug());
$self->do_lookup(%options, json => $content);
$self->do_map(%options);
}
1;
=head1 NAME
Keepass global
=head1 SYNOPSIS
keepass class
=head1 KEEPASS OPTIONS
=over 8
=item B<--keepass-endpoint>
Connection information to be used in keepass file.
=item B<--keepass-endpoint-file>
File with keepass connection informations.
=item B<--keepass-file>
Keepass file.
=item B<--keepass-password>
Keepass master password.
=item B<--keepass-search-value>
Looking for a value in the JSON keepass. Can use JSON Path and other option values.
Example:
--keepass-search-value='password=$..entries.[?($_->{title} =~ /serveurx/i)].password'
--keepass-search-value='username=$..entries.[?($_->{title} =~ /serveurx/i)].username'
--keepass-search-value='password=$..entries.[?($_->{title} =~ /%{hostname}/i)].password'
=item B<--keepass-map-option>
Overload plugin option.
Example:
--keepass-map-option="password=%{password}"
--keepass-map-option="username=%{username}"
=back
=head1 DESCRIPTION
B<keepass>.
=cut
CENTREON_PLUGINS_PASSWORDMGR_KEEPASS
$fatpacked{"centreon/plugins/passwordmgr/teampass.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PASSWORDMGR_TEAMPASS';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::passwordmgr::teampass;
use strict;
use warnings;
use JSON::Path;
use JSON::XS;
use Data::Dumper;
use centreon::plugins::http;
use vars qw($teampass_connections);
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (!defined($options{output})) {
print "Class PasswordMgr: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class PasswordMgr: Need to specify 'options' argument.");
$options{output}->option_exit();
}
$options{options}->add_options(arguments => {
"teampass-endpoint:s" => { name => 'teampass_endpoint' },
"teampass-endpoint-file:s" => { name => 'teampass_endpoint_file' },
"teampass-api-key:s" => { name => 'teampass_api_key' },
"teampass-api-address:s" => { name => 'teampass_api_address' },
"teampass-api-request:s" => { name => 'teampass_api_request' },
"teampass-search-value:s@" => { name => 'teampass_search_value' },
"teampass-map-option:s@" => { name => 'teampass_map_option' },
"teampass-timeout:s" => { name => 'teampass_timeout' },
});
$options{options}->add_help(package => __PACKAGE__, sections => 'TEAMPASS OPTIONS');
$self->{output} = $options{output};
$self->{http} = centreon::plugins::http->new(%options, noptions => 1, default_backend => 'curl');
$JSON::Path::Safe = 0;
return $self;
}
sub build_api_args {
my ($self, %options) = @_;
$self->{connection_info} = { address => undef, key => undef, request => undef };
if (defined($options{option_results}->{teampass_endpoint_file}) && $options{option_results}->{teampass_endpoint_file} ne '') {
if (! -f $options{option_results}->{teampass_endpoint_file} or ! -r $options{option_results}->{teampass_endpoint_file}) {
$self->{output}->add_option_msg(short_msg => "Cannot read teampass file: $!");
$self->{output}->option_exit();
}
require $options{option_results}->{teampass_endpoint_file};
if (defined($teampass_connections) && defined($options{option_results}->{teampass_endpoint}) && $options{option_results}->{teampass_endpoint} ne '') {
if (!defined($teampass_connections->{$options{option_results}->{teampass_endpoint}})) {
$self->{output}->add_option_msg(short_msg => "Endpoint $options{option_results}->{teampass_endpoint} doesn't exist in teampass file");
$self->{output}->option_exit();
}
$self->{connection_info} = $teampass_connections->{$options{option_results}->{teampass_endpoint}};
}
}
foreach (['teampass_api_address', 'address'], ['teampass_api_key', 'key'], ['teampass_api_request', 'request']) {
if (defined($options{option_results}->{$_->[0]}) && $options{option_results}->{$_->[0]} ne '') {
$self->{connection_info}->{$_->[1]} = $options{option_results}->{$_->[0]};
}
}
if (defined($self->{connection_info}->{address}) && $self->{connection_info}->{address} ne '') {
foreach ('key', 'request') {
if (!defined($self->{connection_info}->{$_}) || $self->{connection_info}->{$_} eq '') {
$self->{output}->add_option_msg(short_msg => "Please set teampass-api-$_ option");
$self->{output}->option_exit();
}
}
}
}
sub do_lookup {
my ($self, %options) = @_;
$self->{lookup_values} = {};
return if (!defined($options{option_results}->{teampass_search_value}));
foreach (@{$options{option_results}->{teampass_search_value}}) {
next if (! /^(.+?)=(.+)$/);
my ($map, $lookup) = ($1, $2);
# Change %{xxx} options usage
while ($lookup =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $options{option_results}->{$1} if (defined($options{option_results}->{$1}));
$lookup =~ s/\%\{$1\}/$sub/g
}
my $jpath = JSON::Path->new($lookup);
my $result = $jpath->value($options{json});
$self->{output}->output_add(long_msg => 'lookup = ' . $lookup. ' - response = ' . Data::Dumper::Dumper($result), debug => 1);
$self->{lookup_values}->{$map} = $result;
}
}
sub do_map {
my ($self, %options) = @_;
return if (!defined($options{option_results}->{teampass_map_option}));
foreach (@{$options{option_results}->{teampass_map_option}}) {
next if (! /^(.+?)=(.+)$/);
my ($option, $map) = ($1, $2);
# Change %{xxx} options usage
while ($map =~ /\%\{(.*?)\}/g) {
my $sub = '';
$sub = $self->{lookup_values}->{$1} if (defined($self->{lookup_values}->{$1}));
$map =~ s/\%\{$1\}/$sub/g
}
$option =~ s/-/_/g;
$options{option_results}->{$option} = $map;
}
}
sub manage_options {
my ($self, %options) = @_;
$self->build_api_args(%options);
return if (!defined($self->{connection_info}->{address}));
$self->{http}->set_options(
timeout => $options{option_results}->{teampass_timeout},
unknown_status => '%{http_code} < 200 or %{http_code} >= 300',
);
my $response = $self->{http}->request(method => 'GET',
full_url => $self->{connection_info}->{address} . $self->{connection_info}->{request},
hostname => '',
get_param => ['apikey=' . $self->{connection_info}->{key}],
);
$self->{output}->output_add(long_msg => $response, debug => 1);
my $json;
eval {
$json = JSON::XS->new->utf8->decode($response);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot decode teampass json response: $@");
$self->{output}->option_exit();
}
$self->do_lookup(%options, json => $json);
$self->do_map(%options);
}
1;
=head1 NAME
Teampass global
=head1 SYNOPSIS
teampass class
=head1 TEAMPASS OPTIONS
=over 8
=item B<--teampass-endpoint>
Connection information to be used in teampass file.
=item B<--teampass-endpoint-file>
File with teampass connection informations.
=item B<--teampass-timeout>
Set HTTP Rest API timeout (default: 5).
=item B<--teampass-api-key>
Teampass API Key.
=item B<--teampass-api-address>
Teampass URL (example: http://10.0.0.1/teampass).
=item B<--teampass-api-request>
Teampass request (example: /api/index.php/folder/3).
=item B<--teampass-search-value>
Looking for a value in the JSON teampass response. Can use JSON Path and other option values.
Example:
--teampass-search-value='password=$.[?($_->{label} =~ /serveur1/i)].pw'
--teampass-search-value='login=$.[?($_->{label} =~ /serveur1/i)].login'
--teampass-search-value='password=$.[?($_->{label} =~ /%{hostname}/i)].pw'
=item B<--teampass-map-option>
Overload plugin option.
Example:
--teampass-map-option="password=%{password}"
--teampass-map-option="username=%{login}"
=back
=head1 DESCRIPTION
B<teampass>.
=cut
CENTREON_PLUGINS_PASSWORDMGR_TEAMPASS
$fatpacked{"centreon/plugins/perfdata.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_PERFDATA';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::perfdata;
use strict;
use warnings;
use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{output} = $options{output};
# Typical Nagios Perfdata 'with ~ @ ..'
$self->{threshold_label} = {};
$self->{float_precision} = defined($self->{output}->{option_results}->{float_precision}) && $self->{output}->{option_results}->{float_precision} =~ /\d+/ ?
int($self->{output}->{option_results}->{float_precision}) : 8;
return $self;
}
sub get_perfdata_for_output {
my ($self, %options) = @_;
# $options{label} : threshold label
# $options{total} : percent threshold to transform in global
# $options{cast_int} : cast absolute to int
# $options{op} : operator to apply to start/end value (uses with 'value'})
# $options{value} : value to apply with 'op' option
if (!defined($self->{threshold_label}->{$options{label}}->{value}) || $self->{threshold_label}->{$options{label}}->{value} eq '') {
return '';
}
my %perf_value = %{$self->{threshold_label}->{$options{label}}};
if (defined($options{op}) && defined($options{value})) {
eval "\$perf_value{start} = \$perf_value{start} $options{op} \$options{value}" if ($perf_value{infinite_neg} == 0);
eval "\$perf_value{end} = \$perf_value{end} $options{op} \$options{value}" if ($perf_value{infinite_pos} == 0);
}
if (defined($options{total})) {
$perf_value{start} = $perf_value{start} * $options{total} / 100 if ($perf_value{infinite_neg} == 0);
$perf_value{end} = $perf_value{end} * $options{total} / 100 if ($perf_value{infinite_pos} == 0);
$perf_value{start} = sprintf("%.2f", $perf_value{start}) if ($perf_value{infinite_neg} == 0 && (!defined($options{cast_int}) || $options{cast_int} != 1));
$perf_value{end} = sprintf("%.2f", $perf_value{end}) if ($perf_value{infinite_pos} == 0 && (!defined($options{cast_int}) || $options{cast_int} != 1));
}
$perf_value{start} = int($perf_value{start}) if ($perf_value{infinite_neg} == 0 && defined($options{cast_int}) && $options{cast_int} == 1);
$perf_value{end} = int($perf_value{end}) if ($perf_value{infinite_pos} == 0 && defined($options{cast_int}) && $options{cast_int} == 1);
my $perf_output = ($perf_value{arobase} == 1 ? '@' : '') .
(($perf_value{infinite_neg} == 0) ? $perf_value{start} : '~') .
':' .
(($perf_value{infinite_pos} == 0) ? $perf_value{end} : '');
return $perf_output;
}
sub threshold_validate {
my ($self, %options) = @_;
# $options{label} : threshold label
# $options{value} : threshold value
my $status = 1;
$self->{threshold_label}->{$options{label}} = { value => $options{value}, start => undef, end => undef, arobase => undef, infinite_neg => undef, infinite_pos => undef };
if (!defined($options{value}) || $options{value} eq '') {
return $status;
}
($status, my $result_perf) =
centreon::plugins::misc::parse_threshold(threshold => $options{value});
$self->{threshold_label}->{$options{label}} = { %{$self->{threshold_label}->{$options{label}}}, %$result_perf };
$self->{threshold_label}->{$options{label}}->{start_precision} = $self->{threshold_label}->{$options{label}}->{start};
if ($self->{threshold_label}->{$options{label}}->{start} =~ /[.,]/) {
$self->{threshold_label}->{$options{label}}->{start_precision} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $self->{threshold_label}->{$options{label}}->{start});
}
$self->{threshold_label}->{$options{label}}->{end_precision} = $self->{threshold_label}->{$options{label}}->{end};
if ($self->{threshold_label}->{$options{label}}->{end} =~ /[.,]/) {
$self->{threshold_label}->{$options{label}}->{end_precision} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $self->{threshold_label}->{$options{label}}->{end});
}
return $status;
}
sub threshold_check {
my ($self, %options) = @_;
# Can check multiple threshold. First match: out. Order is important
# options{value}: value to compare
# options{threshold}: ref to an array (example: [ {label => 'warning', exit_litteral => 'warning' }, {label => 'critical', exit_litteral => 'critical'} ]
if ($options{value} =~ /[.,]/) {
$options{value} = sprintf("%.$self->{output}->{option_results}->{float_precision}f", $options{value});
}
foreach (@{$options{threshold}}) {
next if (!defined($self->{threshold_label}->{$_->{label}}));
next if (!defined($self->{threshold_label}->{$_->{label}}->{value}) || $self->{threshold_label}->{$_->{label}}->{value} eq '');
if ($self->{threshold_label}->{$_->{label}}->{arobase} == 0 && ($options{value} < $self->{threshold_label}->{$_->{label}}->{start_precision} || $options{value} > $self->{threshold_label}->{$_->{label}}->{end_precision})) {
return $_->{exit_litteral};
} elsif ($self->{threshold_label}->{$_->{label}}->{arobase} == 1 && ($options{value} >= $self->{threshold_label}->{$_->{label}}->{start_precision} && $options{value} <= $self->{threshold_label}->{$_->{label}}->{end_precision})) {
return $_->{exit_litteral};
}
}
return 'ok';
}
sub trim {
my ($self, $value) = @_;
$value =~ s/^[ \t]+//;
$value =~ s/[ \t]+$//;
return $value;
}
sub change_bytes {
my ($self, %options) = @_;
my $value = $options{value};
my $divide = defined($options{network}) ? 1000 : 1024;
my @units = ('K', 'M', 'G', 'T');
my $unit = '';
my $sign = '';
$sign = '-' if ($value != abs($value));
$value = abs($value);
for (my $i = 0; $i < scalar(@units); $i++) {
last if (($value / $divide) < 1);
$unit = $units[$i];
$value = $value / $divide;
}
return (sprintf('%.2f', $sign . $value), $unit . (defined($options{network}) ? 'b' : 'B'));
}
1;
=head1 NAME
Perfdata class
=head1 SYNOPSIS
-
=head1 DESCRIPTION
B<perfdata>.
=cut
CENTREON_PLUGINS_PERFDATA
$fatpacked{"centreon/plugins/script.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SCRIPT';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::script;
use strict;
use warnings;
use centreon::plugins::output;
use centreon::plugins::misc;
use Pod::Usage;
my %handlers = (DIE => {}, ALRM => {});
my $global_version = '20250900 (446490d)';
my $alternative_fatpacker = 1;
sub new {
my ($class) = @_;
my $self = {};
bless $self, $class;
$self->{options} = undef;
$self->{plugin} = undef;
$self->{help} = undef;
# Avoid to destroy because it keeps a ref on the object.
# A problem if we execute it multiple times in the same perl execution
# Use prepare_destroy
$self->set_signal_handlers();
return $self;
}
sub prepare_destroy {
my ($self) = @_;
%handlers = ();
}
sub set_signal_handlers {
my ($self) = @_;
$SIG{__DIE__} = \&class_handle_DIE;
$handlers{DIE}->{$self} = sub { $self->handle_DIE($_[0]) };
}
sub class_handle_DIE {
my ($msg) = @_;
foreach (keys %{$handlers{DIE}}) {
&{$handlers{DIE}->{$_}}($msg);
}
}
sub class_handle_ALRM {
foreach (keys %{$handlers{ALRM}}) {
&{$handlers{ALRM}->{$_}}();
}
}
sub handle_DIE {
my ($self, $msg) = @_;
return unless defined $^S and $^S == 0; # Ignore errors in eval
$self->{output}->add_option_msg(short_msg => $msg);
$self->{output}->die_exit();
}
sub handle_ALRM {
my ($self) = @_;
$self->{output}->add_option_msg(short_msg => 'script global timeout');
$self->{output}->option_exit();
}
sub get_global_version {
return $global_version;
}
sub get_plugin {
my ($self) = @_;
# Need to load global 'Output' and 'Options'
if ($alternative_fatpacker == 0) {
require centreon::plugins::options;
$self->{options} = centreon::plugins::options->new();
} else {
require centreon::plugins::alternative::FatPackerOptions;
$self->{options} = centreon::plugins::alternative::FatPackerOptions->new();
}
$self->{output} = centreon::plugins::output->new(options => $self->{options});
$self->{options}->set_output(output => $self->{output});
$self->{options}->add_options(arguments => {
'plugin:s' => { name => 'plugin' },
'list-plugin' => { name => 'list_plugin' },
'help' => { name => 'help' },
'ignore-warn-msg' => { name => 'ignore_warn_msg' },
'version' => { name => 'version' },
'runas:s' => { name => 'runas' },
'global-timeout:s' => { name => 'global_timeout' },
'environment:s%' => { name => 'environment' },
'convert-args:s' => { name => 'convert_args' },
});
$self->{options}->parse_options();
$self->{plugin} = $self->{options}->get_option(argument => 'plugin');
$self->{list_plugin} = $self->{options}->get_option(argument => 'list_plugin');
$self->{help} = $self->{options}->get_option(argument => 'help');
$self->{version} = $self->{options}->get_option(argument => 'version');
$self->{runas} = $self->{options}->get_option(argument => 'runas');
$self->{environment} = $self->{options}->get_option(argument => 'environment');
$self->{ignore_warn_msg} = $self->{options}->get_option(argument => 'ignore_warn_msg');
$self->{convert_args} = $self->{options}->get_option(argument => 'convert_args');
my $global_timeout = $self->{options}->get_option(argument => 'global_timeout');
if (defined($global_timeout) && $global_timeout =~ /(\d+)/) {
$SIG{ALRM} = \&class_handle_ALRM;
$handlers{ALRM}->{$self} = sub { $self->handle_ALRM() };
alarm($1);
}
$self->{output}->plugin(name => $self->{plugin});
$self->{output}->check_options(option_results => $self->{options}->get_options());
$self->{options}->clean();
}
sub convert_args {
my ($self) = @_;
if ($self->{convert_args} =~ /^(.+?),(.*)/) {
my ($search, $replace) = ($1, $2);
for (my $i = 0; $i <= $#ARGV; $i++) {
eval "\$ARGV[\$i] =~ s/$search/$replace/g";
}
}
}
sub display_local_help {
my ($self) = @_;
my $stdout;
if ($self->{help}) {
local *STDOUT;
open STDOUT, '>', \$stdout;
if ($alternative_fatpacker == 0) {
pod2usage(-exitval => 'NOEXIT', -input => $self->{options}->pod_where(package => __PACKAGE__));
} else {
my $pp = __PACKAGE__ . '.pm';
$pp =~ s{::}{/}g;
my $content_class = $INC{$pp}->{$pp};
open my $str_fh, '<', \$content_class;
pod2usage(-exitval => 'NOEXIT', -input => $str_fh);
close $str_fh;
}
}
$self->{output}->add_option_msg(long_msg => $stdout) if (defined($stdout));
}
sub check_directory {
my ($self, $directory) = @_;
opendir(my $dh, $directory) || return ;
while (my $filename = readdir $dh) {
$self->check_directory($directory . '/' . $filename) if ($filename !~ /^\./ && -d $directory . '/' . $filename);
if ($filename eq 'plugin.pm') {
my $stdout = '';
{
local *STDOUT;
open STDOUT, '>', \$stdout;
pod2usage(
-exitval => 'NOEXIT',
-input => $directory . "/" . $filename,
-verbose => 99,
-sections => 'PLUGIN DESCRIPTION'
);
}
$self->{plugins_result}->{$directory . '/' . $filename} = $stdout;
}
}
closedir $dh;
}
sub fatpacker_find_plugin {
my ($self) = @_;
my $plugins = [];
foreach (@INC) {
next if (ref($_) !~ /FatPacked/);
foreach my $name (keys %$_) {
if ($name =~ /plugin.pm$/) {
push @$plugins, $name;
}
}
}
return $plugins;
}
sub check_plugin_option {
my ($self) = @_;
if (defined($self->{version})) {
$self->{output}->add_option_msg(short_msg => 'Global Version: ' . $global_version);
$self->{output}->option_exit(nolabel => 1);
}
my $no_plugin = 1;
if ($alternative_fatpacker == 1) {
my $integrated_plugins = $self->fatpacker_find_plugin();
if (scalar(@$integrated_plugins) == 1) {
$self->{plugin} = $integrated_plugins->[0];
$no_plugin = 0;
}
}
if ($no_plugin == 1) {
$self->{output}->add_option_msg(short_msg => "Need to specify '--plugin' option.");
$self->{output}->option_exit();
}
}
sub display_list_plugin {
my ($self) = @_;
$self->{plugins_result} = {};
if ($alternative_fatpacker == 1) {
my $integrated_plugins = $self->fatpacker_find_plugin();
foreach my $key (sort @$integrated_plugins) {
# Need to load it to get the description
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => $key,
error_msg => 'Cannot load module --plugin.'
);
my $name = $key;
$name =~ s/\.pm//g;
$name =~ s/\//::/g;
$self->{output}->add_option_msg(long_msg => '-----------------');
$self->{output}->add_option_msg(long_msg => 'PLUGIN: ' . $name);
{
my $stdout = '';
local *STDOUT;
open STDOUT, '>', \$stdout;
my $content_class = $INC{$key}->{$key};
open my $str_fh, '<', \$content_class;
pod2usage(-exitval => 'NOEXIT', -input => $str_fh, -verbose => 99, -sections => 'PLUGIN DESCRIPTION');
close $str_fh;
$self->{output}->add_option_msg(long_msg => $stdout);
}
}
return ;
}
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'FindBin',
error_msg => "Cannot load module 'FindBin'."
);
my $directory = $FindBin::Bin;
if (defined($ENV{PAR_TEMP})) {
$directory = $ENV{PAR_TEMP} . '/inc/lib';
}
# Search file 'plugin.pm'
$self->check_directory($directory);
foreach my $key (sort keys %{$self->{plugins_result}}) {
my $name = $key;
$name =~ s/^\Q$directory\E\/(.*)\.pm/$1/;
$name =~ s/\//::/g;
$self->{plugins_result}->{$key} =~ s/^Plugin Description/DESCRIPTION/i;
$self->{output}->add_option_msg(long_msg => '-----------------');
$self->{output}->add_option_msg(long_msg => 'PLUGIN: ' . $name);
$self->{output}->add_option_msg(long_msg => $self->{plugins_result}->{$key});
}
}
sub check_relaunch_get_args {
my ($self) = @_;
my $args = ['--plugin=' . $self->{plugin}, @ARGV];
push @$args, '--ignore-warn-msg' if (defined($self->{ignore_warn_msg}));
push @$args, '--help' if (defined($self->{help}));
push @$args, '--global-timeout', $self->{global_timeout} if (defined($self->{global_timeout}));
foreach ((
['output_xml', 0], ['output_json', 0], ['output_openmetrics', 0],
['disco_format', 0], ['disco_show', 0], ['use_new_perfdata', 0], ['debug', 0], ['verbose', 0],
['range_perfdata', 1], ['filter_uom', 1], ['opt_exit', 1], ['filter_perfdata', 1],
['output_file', 1], ['float_precision', 1]
)) {
my $option = $self->{output}->get_option(option => $_->[0]);
if (defined($option)) {
my $option_label = $_->[0];
$option_label =~ s/_/-/g;
push @$args, "--$option_label" if ($_->[1] == 0);
push @$args, "--$option_label", $option if ($_->[1] == 1);
}
}
return $args;
}
sub check_relaunch {
my $self = shift;
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'FindBin',
error_msg => "Cannot load module 'FindBin'."
);
my $need_restart = 0;
my $cmd = $FindBin::Bin . '/' . $FindBin::Script;
my $args = [];
if (defined($self->{environment})) {
foreach (keys %{$self->{environment}}) {
if ($_ ne '' && (!defined($ENV{$_}) || $ENV{$_} ne $self->{environment}->{$_})) {
$ENV{$_} = $self->{environment}->{$_};
$need_restart = 1;
}
}
}
my $rebuild_args = $self->check_relaunch_get_args();
if (defined($self->{runas}) && $self->{runas} ne '') {
# Check if it's already me and user exist ;)
my ($name, $passwd, $uid) = getpwnam($self->{runas});
if (!defined($uid)) {
$self->{output}->add_option_msg(short_msg => "Runas user '" . $self->{runas} . "' not exist.");
$self->{output}->option_exit();
}
if ($uid != $>) {
if ($> == 0) {
unshift @$args, '-s', '/bin/bash', '-l', $self->{runas}, '-c', join(' ', $cmd, @$rebuild_args);
$cmd = 'su';
} else {
unshift @$args, '-S', '-u', $self->{runas}, $cmd, @$rebuild_args;
$cmd = 'sudo';
}
$need_restart = 1;
}
}
if ($need_restart == 1) {
if (scalar(@$args) <= 0) {
unshift @$args, @$rebuild_args;
}
my ($lerror, $stdout, $exit_code) = centreon::plugins::misc::backtick(
command => $cmd,
arguments => $args,
timeout => 30,
wait_exit => 1
);
if ($exit_code <= -1000) {
if ($exit_code == -1000) {
$self->{output}->output_add(
severity => 'UNKNOWN',
short_msg => $stdout
);
}
$self->{output}->display();
$self->{output}->exit();
}
chomp $stdout;
print $stdout . "\n";
# We put unknown
if (!($exit_code >= 0 && $exit_code <= 4)) {
exit 3;
}
exit $exit_code;
}
}
sub run {
my ($self) = @_;
$self->get_plugin();
if (defined($self->{help}) && !defined($self->{plugin})) {
$self->display_local_help();
$self->{output}->option_exit();
}
if (defined($self->{list_plugin})) {
$self->display_list_plugin();
$self->{output}->option_exit();
}
$self->check_plugin_option() if (!defined($self->{plugin}) || $self->{plugin} eq '');
if (defined($self->{ignore_warn_msg})) {
$SIG{__WARN__} = sub {};
}
$self->convert_args() if (defined($self->{convert_args}));
$self->check_relaunch();
(undef, $self->{plugin}) =
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => $self->{plugin},
error_msg => 'Cannot load module --plugin.'
);
my $plugin = $self->{plugin}->new(options => $self->{options}, output => $self->{output});
$plugin->init(
help => $self->{help},
version => $self->{version}
);
$plugin->run();
}
1;
=head1 NAME
centreon_plugins.pl - main program to call Centreon plugins.
=head1 SYNOPSIS
centreon_plugins.pl [options]
=head1 OPTIONS
=over 8
=item B<--plugin>
Specify the path to the plugin.
=item B<--list-plugin>
List all available plugins.
=item B<--version>
Return the version of the plugin.
=item B<--help>
Return the help message for the plugin and exit.
=item B<--ignore-warn-msg>
Ignore Perl warning messages (they will not be displayed).
=item B<--runas>
Run the script as a different user.
=item B<--global-timeout>
Define the script's timeout.
=item B<--environment>
Define environment variables for the script (set them in the execution environment
before running it for better performance).
=item B<--convert-args>
Replace a pattern in the provided arguments. Useful to bypass forbidden characters.
E.g.: when a password transmitted via the NRPE protocol contains '!' (which is
interpreted as a separator), you can send '##' instead of the '!' and the plugin will replace '##' with '!', using the --convert-args='##,\x21' option.
=back
=head1 DESCRIPTION
B<centreon_plugins.pl> .
=cut
CENTREON_PLUGINS_SCRIPT
$fatpacked{"centreon/plugins/script_sql.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_SCRIPT_SQL';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::script_sql;
use strict;
use warnings;
use centreon::plugins::misc;
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{options} = $options{options};
$self->{output} = $options{output};
$self->{options}->add_options(
arguments => {
'mode:s' => { name => 'mode_name' },
'dyn-mode:s' => { name => 'dynmode_name' },
'list-mode' => { name => 'list_mode' },
'mode-version:s' => { name => 'mode_version' },
'sqlmode:s' => { name => 'sqlmode_name', default => 'dbi' },
'list-sqlmode' => { name => 'list_sqlmode' },
'multiple' => { name => 'multiple' },
'no-sanity-options' => { name => 'no_sanity_options' },
'pass-manager:s' => { name => 'pass_manager' }
}
);
$self->{version} = '1.0';
$self->{modes} = {};
$self->{sql_modes} = { 'dbi' => 'centreon::plugins::dbi' };
$self->{default} = undef;
$self->{sqldefault} = {};
$self->{sqlmode_current} = undef;
$self->{sqlmode_stored} = [];
$self->{options}->parse_options();
$self->{option_results} = $self->{options}->get_options();
foreach (keys %{$self->{option_results}}) {
$self->{$_} = $self->{option_results}->{$_};
}
$self->{options}->clean();
$self->{options}->add_help(package => $options{package}, sections => 'PLUGIN DESCRIPTION');
$self->{options}->add_help(package => __PACKAGE__, sections => 'GLOBAL OPTIONS');
$self->{output}->mode(name => $self->{mode_name});
return $self;
}
sub init {
my ($self, %options) = @_;
# add meta mode
$self->{modes}->{multi} = 'centreon::plugins::multi';
if (defined($options{help}) && !defined($self->{mode_name}) && !defined($self->{dynmode_name})) {
$self->{options}->display_help();
$self->{output}->option_exit();
}
if (defined($options{version}) && !defined($self->{mode_name}) && !defined($self->{dynmode_name})) {
$self->version();
}
if (defined($self->{list_mode})) {
$self->list_mode();
}
if (defined($self->{list_sqlmode})) {
$self->list_sqlmode();
}
$self->{options}->set_sanity() if (!defined($self->{no_sanity_options}));
# Output HELP
$self->{options}->add_help(package => 'centreon::plugins::output', sections => 'OUTPUT OPTIONS');
$self->load_password_mgr();
if (defined($self->{sqlmode_name}) && $self->{sqlmode_name} ne '') {
$self->is_sqlmode(sqlmode => $self->{sqlmode_name});
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => $self->{sql_modes}->{$self->{sqlmode_name}},
error_msg => "Cannot load module --sqlmode."
);
$self->{sqlmode_current} = $self->{sql_modes}->{$self->{sqlmode_name}}->new(
options => $self->{options},
output => $self->{output},
sqlmode_name => $self->{sqlmode_name},
mode_name => $self->{mode_name}
);
} else {
$self->{output}->add_option_msg(short_msg => "Need to specify '--sqlmode'.");
$self->{output}->option_exit();
}
# Load mode
if (defined($self->{mode_name}) && $self->{mode_name} ne '') {
$self->is_mode(mode => $self->{mode_name});
centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $self->{modes}{$self->{mode_name}},
error_msg => "Cannot load module --mode.");
$self->{mode} = $self->{modes}{$self->{mode_name}}->new(options => $self->{options}, output => $self->{output}, mode => $self->{mode_name});
} elsif (defined($self->{dynmode_name}) && $self->{dynmode_name} ne '') {
(undef, $self->{dynmode_name}) = centreon::plugins::misc::mymodule_load(output => $self->{output}, module => $self->{dynmode_name},
error_msg => "Cannot load module --dyn-mode.");
$self->{mode} = $self->{dynmode_name}->new(options => $self->{options}, output => $self->{output}, mode => $self->{dynmode_name});
} else {
$self->{output}->add_option_msg(short_msg => "Need to specify '--mode' or '--dyn-mode' option.");
$self->{output}->option_exit();
}
if (defined($options{help})) {
if (defined($self->{mode_name}) && $self->{mode_name} ne '') {
$self->{options}->add_help(package => $self->{modes}{$self->{mode_name}}, sections => 'MODE');
} else {
$self->{options}->add_help(package => $self->{dynmode_name}, sections => 'MODE');
}
$self->{options}->display_help();
$self->{output}->option_exit();
}
if (defined($options{version})) {
$self->{mode}->version();
$self->{output}->option_exit(nolabel => 1);
}
if (centreon::plugins::misc::minimal_version($self->{mode}->{version}, $self->{mode_version}) == 0) {
$self->{output}->add_option_msg(short_msg => "Not good version for plugin mode. Excepted at least: " . $self->{mode_version} . ". Get: ". $self->{mode}->{version});
$self->{output}->option_exit();
}
$self->{options}->parse_options();
$self->{option_results} = $self->{options}->get_options();
$self->{pass_mgr}->manage_options(option_results => $self->{option_results}) if (defined($self->{pass_mgr}));
push @{$self->{sqlmode_stored}}, $self->{sqlmode_current};
$self->{sqlmode_current}->set_options(option_results => $self->{option_results});
$self->{sqlmode_current}->set_defaults(default => $self->{sqldefault});
while ($self->{sqlmode_current}->check_options()) {
$self->{sqlmode_current} = $self->{sql_modes}->{$self->{sqlmode_name}}->new(noptions => 1, options => $self->{options}, output => $self->{output}, mode => $self->{sqlmode_name});
$self->{sqlmode_current}->set_options(option_results => $self->{option_results});
push @{$self->{sqlmode_stored}}, $self->{sqlmode_current};
}
$self->{mode}->check_options(
option_results => $self->{option_results},
default => $self->{default},
modes => $self->{modes} # for meta mode multi
);
}
sub load_password_mgr {
my ($self, %options) = @_;
return if (!defined($self->{option_results}->{pass_manager}) || $self->{option_results}->{pass_manager} eq '');
(undef, my $pass_mgr_name) = centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => "centreon::plugins::passwordmgr::" . $self->{option_results}->{pass_manager},
error_msg => "Cannot load module 'centreon::plugins::passwordmgr::" . $self->{option_results}->{pass_manager} . "'"
);
$self->{pass_mgr} = $pass_mgr_name->new(options => $self->{options}, output => $self->{output});
}
sub run {
my $self = shift;
if ($self->{output}->is_disco_format()) {
$self->{mode}->disco_format();
$self->{output}->display_disco_format();
$self->{output}->exit(exit_litteral => 'ok');
}
if ($self->{output}->is_disco_show()) {
if (defined($self->{multiple})) {
$self->{mode}->disco_show(sql => $self->{sqlmode});
} else {
$self->{mode}->disco_show(sql => $self->{sqlmode_stored}[0]);
}
$self->{output}->display_disco_show();
$self->{output}->exit(exit_litteral => 'ok');
} else {
if (defined($self->{multiple})) {
$self->{mode}->run(sql => $self->{sqlmode_stored});
} else {
$self->{mode}->run(sql => $self->{sqlmode_stored}[0]);
}
}
}
sub is_mode {
my ($self, %options) = @_;
# $options->{mode} = mode
if (!defined($self->{modes}{$options{mode}})) {
$self->{output}->add_option_msg(short_msg => "mode '" . $options{mode} . "' doesn't exist (use --list-mode option to show available modes).");
$self->{output}->option_exit();
}
}
sub is_sqlmode {
my ($self, %options) = @_;
# $options->{sqlmode} = mode
if (!defined($self->{sql_modes}->{$options{sqlmode}})) {
$self->{output}->add_option_msg(short_msg => "mode '" . $options{sqlmode} . "' doesn't exist (use --list-sqlmode option to show available modes).");
$self->{output}->option_exit();
}
}
sub version {
my $self = shift;
$self->{output}->add_option_msg(short_msg => "Plugin Version: " . $self->{version});
$self->{output}->option_exit(nolabel => 1);
}
sub list_mode {
my $self = shift;
$self->{options}->display_help();
$self->{output}->add_option_msg(long_msg => 'Modes Meta:');
$self->{output}->add_option_msg(long_msg => ' multi');
$self->{output}->add_option_msg(long_msg => '');
$self->{output}->add_option_msg(long_msg => 'Modes Available:');
foreach (sort keys %{$self->{modes}}) {
next if ($_ eq 'multi');
$self->{output}->add_option_msg(long_msg => ' ' . $_);
}
$self->{output}->option_exit(nolabel => 1);
}
sub list_sqlmode {
my $self = shift;
$self->{options}->display_help();
$self->{output}->add_option_msg(long_msg => "SQL Modes Available:");
foreach (keys %{$self->{sql_modes}}) {
$self->{output}->add_option_msg(long_msg => " " . $_);
}
$self->{output}->option_exit(nolabel => 1);
}
1;
=head1 NAME
-
=head1 SYNOPSIS
-
=head1 GLOBAL OPTIONS
=over 8
=item B<--mode>
Define the mode in which you want the plugin to be executed (see --list-mode).
=item B<--dyn-mode>
Specify a mode with the module's path (advanced).
=item B<--list-mode>
List all available modes.
=item B<--mode-version>
Check minimal version of mode. If not, unknown error.
=item B<--version>
Return the version of the plugin.
=item B<--sqlmode>
This plugin offers several ways to query the database (default: dbi).
See --list-sqlmode.
=item B<--list-sqlmode>
List all available sql modes.
=item B<--multiple>
Enable connecting to multiple databases (required by some specific modes such as replication).
=item B<--pass-manager>
Define the password manager you want to use.
Supported managers are: environment, file, keepass, hashicorpvault and teampass.
=back
=head1 DESCRIPTION
B<>.
=cut
CENTREON_PLUGINS_SCRIPT_SQL
$fatpacked{"centreon/plugins/statefile.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_STATEFILE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::statefile;
use strict;
use warnings;
use Data::Dumper;
use centreon::plugins::misc;
my $default_dir = '/var/lib/centreon/centplugins';
if ($^O eq 'MSWin32') {
$default_dir = 'C:/Windows/Temp';
}
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
if (defined($options{options})) {
$options{options}->add_options(arguments => {
'memcached:s' => { name => 'memcached' },
'redis-server:s' => { name => 'redis_server' },
'redis-attribute:s%' => { name => 'redis_attribute' },
'redis-db:s' => { name => 'redis_db' },
'memexpiration:s' => { name => 'memexpiration', default => 86400 },
'statefile-dir:s' => { name => 'statefile_dir', default => $default_dir },
'statefile-suffix:s' => { name => 'statefile_suffix', default => '' },
'statefile-concat-cwd' => { name => 'statefile_concat_cwd' },
'statefile-storable' => { name => 'statefile_storable' }, # legacy
'failback-file' => { name => 'failback_file' },
'statefile-format:s' => { name => 'statefile_format' },
'statefile-key:s' => { name => 'statefile_key' },
'statefile-cipher:s' => { name => 'statefile_cipher' }
});
$options{options}->add_help(package => __PACKAGE__, sections => 'RETENTION OPTIONS', once => 1);
}
$self->{error} = 0;
$self->{output} = $options{output};
$self->{datas} = {};
$self->{storable} = 0;
$self->{memcached_ok} = 0;
$self->{memcached} = undef;
$self->{statefile_dir} = undef;
$self->{statefile_suffix} = undef;
return $self;
}
sub check_options {
my ($self, %options) = @_;
if (defined($options{option_results}) && defined($options{option_results}->{memcached})) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Memcached::libmemcached',
error_msg => "Cannot load module 'Memcached::libmemcached'."
);
$self->{memcached} = Memcached::libmemcached->new();
Memcached::libmemcached::memcached_server_add($self->{memcached}, $options{option_results}->{memcached});
}
# Check redis
if (defined($options{option_results}->{redis_server})) {
$self->{redis_attributes} = '';
if (defined($options{option_results}->{redis_attribute})) {
foreach (keys %{$options{option_results}->{redis_attribute}}) {
$self->{redis_attributes} .= "$_ => " . $options{option_results}->{redis_attribute}->{$_} . ', ';
}
}
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Redis',
error_msg => "Cannot load module 'Redis'."
);
eval {
$options{option_results}->{redis_server} .= ':6379' if ($options{option_results}->{redis_server} !~ /:\d+$/);
$self->{redis_cnx} = Redis->new(
server => $options{option_results}->{redis_server},
eval $self->{redis_attributes}
);
if (defined($self->{redis_cnx}) &&
defined($options{option_results}->{redis_db}) &&
$options{option_results}->{redis_db} ne ''
) {
$self->{redis_cnx}->select($options{option_results}->{redis_db});
}
};
if (!defined($self->{redis_cnx}) && !defined($options{option_results}->{failback_file})) {
$self->{output}->add_option_msg(short_msg => "redis connection issue: $@");
$self->{output}->option_exit();
}
}
$self->{statefile_format} = 'json';
if (defined($options{option_results}->{statefile_format}) && $options{option_results}->{statefile_format} ne '' &&
$options{option_results}->{statefile_format} =~ /^(?:dumper|json|storable)$/) {
$self->{statefile_format} = $options{option_results}->{statefile_format};
} elsif (defined($options{default_format}) && $options{default_format} =~ /^(?:dumper|json|storable)$/) {
$self->{statefile_format} = $options{default_format};
}
if (defined($options{option_results}->{statefile_storable})) {
$self->{statefile_format} = 'storable';
}
if ($self->{statefile_format} eq 'dumper') {
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'Safe',
no_quit => 1
);
$self->{safe} = Safe->new();
$self->{safe}->share('$datas');
} elsif ($self->{statefile_format} eq 'storable') {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Storable',
error_msg => "Cannot load module 'Storable'."
);
} elsif ($self->{statefile_format} eq 'json') {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'JSON::XS',
error_msg => "Cannot load module 'JSON::XS'."
);
}
$self->{statefile_cipher} = defined($options{option_results}->{statefile_cipher}) && $options{option_results}->{statefile_cipher} ne '' ?
$options{option_results}->{statefile_cipher} : 'AES';
$self->{statefile_key} = defined($options{option_results}->{statefile_key}) && $options{option_results}->{statefile_key} ne '' ?
$options{option_results}->{statefile_key} : '';
if ($self->{statefile_key} ne '') {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Crypt::Mode::CBC',
error_msg => "Cannot load module 'Crypt::Mode::CBC'."
);
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Crypt::PRNG',
error_msg => "Cannot load module 'Crypt::PRNG'."
);
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'MIME::Base64',
error_msg => "Cannot load module 'MIME::Base64'."
);
}
$self->{statefile_dir} = $options{option_results}->{statefile_dir};
if (defined($self->{statefile_dir})
&& $self->{statefile_dir} ne $default_dir
&& defined($options{option_results}->{statefile_concat_cwd})
) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'Cwd',
error_msg => "Cannot load module 'Cwd'."
);
$self->{statefile_dir} = Cwd::cwd() . '/' . $self->{statefile_dir};
}
$self->{statefile_suffix} = $options{option_results}->{statefile_suffix};
$self->{memexpiration} = $options{option_results}->{memexpiration};
}
sub error {
my ($self) = shift;
if (@_) {
$self->{error} = $_[0];
}
return $self->{error};
}
sub get_key {
my ($self, %options) = @_;
my $key = $options{key};
{
use bytes;
my $size = length($key);
my $minsize = Crypt::Cipher->min_keysize($options{cipher});
if ($minsize > $size) {
$key .= "0" x ($minsize - $size);
}
}
return $key;
}
sub decrypt {
my ($self, %options) = @_;
return (1, $options{data}) if (!defined($options{data}->{encrypted}));
my $plaintext;
eval {
my $cipher = Crypt::Mode::CBC->new($options{data}->{cipher}, 1);
$plaintext = $cipher->decrypt(
MIME::Base64::decode_base64($options{data}->{ciphertext}),
$self->get_key(key => $self->{statefile_key}, cipher => $options{data}->{cipher}),
pack('H*', $options{data}->{iv})
);
};
if ($@) {
return 0;
}
return $self->deserialize(data => $plaintext, nocipher => 1);
}
sub deserialize {
my ($self, %options) = @_;
my $deserialized = '';
if ($self->{statefile_format} eq 'dumper') {
our $datas;
$self->{safe}->reval($options{data}, 1);
return 0 if ($@);
$deserialized = $datas;
} elsif ($self->{statefile_format} eq 'storable') {
eval {
$deserialized = Storable::thaw($options{data});
};
return 0 if ($@);
} elsif ($self->{statefile_format} eq 'json') {
eval {
$deserialized = JSON::XS->new->decode($options{data});
};
return 0 if ($@);
}
return 0 if (!defined($deserialized) || ref($deserialized) ne 'HASH');
my $rv = 1;
if ($self->{statefile_key} ne '' && !defined($options{nocipher})) {
($rv, $deserialized) = $self->decrypt(data => $deserialized);
}
return ($rv, $deserialized);
}
sub slurp {
my ($self, %options) = @_;
my $content = do {
local $/ = undef;
if (!open my $fh, '<', $options{file}) {
$self->{output}->add_option_msg(short_msg => "Could not open file $options{file}: $!");
$self->{output}->option_exit();
}
<$fh>;
};
return $content;
}
sub read {
my ($self, %options) = @_;
$self->{statefile_suffix} = defined($options{statefile_suffix}) ? $options{statefile_suffix} : $self->{statefile_suffix};
$self->{statefile_dir} = defined($options{statefile_dir}) ? $options{statefile_dir} : $self->{statefile_dir};
$self->{statefile} = defined($options{statefile}) ? $options{statefile} . $self->{statefile_suffix} : $self->{statefile};
$self->{no_quit} = defined($options{no_quit}) && $options{no_quit} == 1 ? 1 : 0;
my ($data, $rv);
if (defined($self->{memcached})) {
# if "SUCCESS" or "NOT FOUND" is ok. Other with use the file
my $val = Memcached::libmemcached::memcached_get($self->{memcached}, $self->{statefile_dir} . '/' . $self->{statefile});
if (defined($self->{memcached}->errstr) && $self->{memcached}->errstr =~ /^SUCCESS|NOT FOUND$/i) {
$self->{memcached_ok} = 1;
if (defined($val)) {
($rv, $data) = $self->deserialize(data => $val);
$self->{datas} = defined($data) ? $data : {};
return $rv;
}
return 0;
}
}
if (defined($self->{redis_cnx})) {
my $val = $self->{redis_cnx}->get($self->{statefile_dir} . "/" . $self->{statefile});
if (defined($val)) {
($rv, $data) = $self->deserialize(data => $val);
$self->{datas} = defined($data) ? $data : {};
return $rv;
}
return 0;
}
if (! -e $self->{statefile_dir} . '/' . $self->{statefile}) {
if (! -w $self->{statefile_dir} || ! -x $self->{statefile_dir}) {
$self->error(1);
$self->{output}->add_option_msg(short_msg => "Cannot write statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'. Need write/exec permissions on directory.");
if ($self->{no_quit} == 0) {
$self->{output}->option_exit();
}
}
return 0;
} elsif (! -w $self->{statefile_dir} . '/' . $self->{statefile}) {
$self->error(1);
$self->{output}->add_option_msg(short_msg => "Cannot write statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'. Need write permissions on file.");
if ($self->{no_quit} == 0) {
$self->{output}->option_exit();
}
return 1;
} elsif (! -s $self->{statefile_dir} . '/' . $self->{statefile}) {
# Empty file. Not a problem. Maybe plugin not manage not values
return 0;
}
$data = $self->slurp(file => $self->{statefile_dir} . '/' . $self->{statefile});
($rv, $data) = $self->deserialize(data => $data);
$self->{datas} = defined($data) ? $data : {};
return $rv;
}
sub get_string_content {
my ($self, %options) = @_;
return Data::Dumper::Dumper($self->{datas});
}
sub get {
my ($self, %options) = @_;
if (defined($self->{datas}->{ $options{name} })) {
return $self->{datas}->{ $options{name} };
}
return undef;
}
sub encrypt {
my ($self, %options) = @_;
my $data = {
encrypted => 1,
cipher => $self->{statefile_cipher},
iv => Crypt::PRNG::random_bytes_hex(16)
};
eval {
my $cipher = Crypt::Mode::CBC->new($self->{statefile_cipher}, 1);
$data->{ciphertext} = MIME::Base64::encode_base64(
$cipher->encrypt(
$options{data},
$self->get_key(key => $self->{statefile_key}, cipher => $self->{statefile_cipher}),
pack('H*', $data->{iv})
),
''
);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "cipher encrypt error: $@");
$self->{output}->option_exit();
}
return $self->serialize(data => $data, nocipher => 1);
}
sub serialize {
my ($self, %options) = @_;
my $serialized = '';
if ($self->{statefile_format} eq 'dumper') {
$serialized = Data::Dumper->Dump([$options{data}], ['datas']);
} elsif ($self->{statefile_format} eq 'storable') {
$serialized = Storable::freeze($options{data});
} elsif ($self->{statefile_format} eq 'json') {
eval {
$serialized = JSON::XS->new->encode($options{data});
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot serialize statefile '" . $self->{statefile_dir} . "/" . $self->{statefile} . "'");
$self->{output}->option_exit();
}
}
if ($self->{statefile_key} ne '' && !defined($options{nocipher})) {
$serialized = $self->encrypt(data => $serialized);
}
return $serialized;
}
sub write {
my ($self, %options) = @_;
my $serialized = $self->serialize(data => $options{data});
if ($self->{memcached_ok} == 1) {
Memcached::libmemcached::memcached_set(
$self->{memcached},
$self->{statefile_dir} . '/' . $self->{statefile},
$serialized,
$self->{memexpiration}
);
if (defined($self->{memcached}->errstr) && $self->{memcached}->errstr =~ /^SUCCESS$/i) {
return ;
}
}
if (defined($self->{redis_cnx})) {
return if (defined($self->{redis_cnx}->set(
$self->{statefile_dir} . '/' . $self->{statefile},
$serialized,
'EX', $self->{memexpiration}))
);
}
open FILE, '>', $self->{statefile_dir} . '/' . $self->{statefile};
print FILE $serialized;
close FILE;
}
1;
=head1 NAME
centreon::plugins::statefile - A module for managing state files with various storage backends.
=head1 SYNOPSIS
use centreon::plugins::statefile;
my $statefile = centreon::plugins::statefile->new(
output => $output,
options => $options
);
$statefile->check_options(option_results => $option_results);
$statefile->read(statefile => 'my_statefile');
my $data = $statefile->get(name => 'some_key');
$statefile->write(data => { some_key => 'some_value' });
=head1 DESCRIPTION
The `centreon::plugins::statefile` module provides methods to manage state files (files storing the data to keep from an
execution to the next one), supporting various storage backends such as local files, Memcached, and Redis. It also supports encryption and different serialization formats.
=head1 METHODS
=head2 new
my $statefile = centreon::plugins::statefile->new(%options);
Creates a new `centreon::plugins::statefile` object.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<output> - An C<centreon::plugins::output> object to log error messages.
=item * C<options> - A C<centreon::plugins::options> object to add command-line options.
=back
=back
=head2 check_options
$statefile->check_options(%options);
Checks and processes the provided options.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<option_results> - A hash of option results.
=back
=back
=head2 read
$statefile->read(%options);
Reads the state file.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<statefile> - The name of the state file to read.
=item * C<statefile_suffix> - An optional suffix for the state file name.
=item * C<statefile_dir> - An optional directory for the state file.
=item * C<no_quit> - An optional flag to prevent the program from exiting on error.
=back
=back
=head2 write
$statefile->write(%options);
Writes data to the state file.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<data> - A hash reference containing the data to write.
=back
=back
=head2 get
my $value = $statefile->get(%options);
Retrieves a value from the state file data.
=over 4
=item * C<%options> - A hash of options. The following keys are supported:
=over 8
=item * C<name> - The key name of the value to retrieve.
=back
=back
=head2 get_string_content
my $string = $statefile->get_string_content();
Returns the state file data as a string.
=head2 error
my $error = $statefile->error();
Gets or sets the error state.
=over 4
=item * C<$error> - An optional error value to set.
=back
=head1 EXAMPLES
=head2 Creating a Statefile Object
use centreon::plugins::statefile;
my $statefile = centreon::plugins::statefile->new(
output => $output,
options => $options
);
=head2 Checking Options
$statefile->check_options(option_results => $option_results);
=head2 Reading a Statefile
$statefile->read(statefile => 'my_statefile');
=head2 Writing to a Statefile
$statefile->write(data => { some_key => 'some_value' });
=head2 Retrieving a Value
my $value = $statefile->get(name => 'some_key');
=head2 Getting Statefile Data as a String
my $string = $statefile->get_string_content();
=head1 AUTHOR
Centreon
=head1 LICENSE
Licensed under the Apache License, Version 2.0.
=cut
=head1 RETENTION OPTIONS
=over 8
=item B<--memcached>
Memcached server to use (only one server).
=item B<--redis-server>
Redis server to use (only one server). Syntax: address[:port]
=item B<--redis-attribute>
Set Redis Options (--redis-attribute="cnx_timeout=5").
=item B<--redis-db>
Set Redis database index.
=item B<--failback-file>
Fall back on a local file if Redis connection fails.
=item B<--memexpiration>
Time to keep data in seconds (default: 86400).
=item B<--statefile-dir>
Define the cache directory (default: '/var/lib/centreon/centplugins').
=item B<--statefile-suffix>
Define a suffix to customize the statefile name (default: '').
=item B<--statefile-concat-cwd>
If used with the '--statefile-dir' option, the latter's value will be used as
a sub-directory of the current working directory.
Useful on Windows when the plugin is compiled, as the file system and permissions are different from Linux.
=item B<--statefile-format>
Define the format used to store the cache. Available formats: 'dumper', 'storable', 'json' (default).
=item B<--statefile-key>
Define the key to encrypt/decrypt the cache.
=item B<--statefile-cipher>
Define the cipher algorithm to encrypt the cache (default: 'AES').
=back
=head1 DESCRIPTION
B<statefile>.
=cut
CENTREON_PLUGINS_STATEFILE
$fatpacked{"centreon/plugins/templates/catalog_functions.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_CATALOG_FUNCTIONS';
#
# Copyright 2018 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 centreon::plugins::templates::catalog_functions;
use strict;
use warnings;
use Exporter;
our @ISA = qw(Exporter);
our @EXPORT_OK = qw(catalog_status_threshold catalog_status_threshold_ng catalog_status_calc);
sub catalog_status_threshold {
my ($self, %options) = @_;
my $status = 'ok';
my $label = $self->{label};
$label =~ s/-/_/g;
if (defined($self->{instance_mode}->{option_results}->{'ok_' . $label}) && $self->{instance_mode}->{option_results}->{'ok_' . $label} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'ok_' . $label})) {
$status = 'ok';
} elsif (defined($self->{instance_mode}->{option_results}->{'critical_' . $label}) && $self->{instance_mode}->{option_results}->{'critical_' . $label} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'critical_' . $label})) {
$status = 'critical';
} elsif (defined($self->{instance_mode}->{option_results}->{'warning_' . $label}) && $self->{instance_mode}->{option_results}->{'warning_' . $label} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'warning_' . $label})) {
$status = 'warning';
} elsif (defined($self->{instance_mode}->{option_results}->{'unknown_' . $label}) && $self->{instance_mode}->{option_results}->{'unknown_' . $label} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'unknown_' . $label})) {
$status = 'unknown';
}
return $status;
}
sub catalog_status_threshold_ng {
my ($self, %options) = @_;
my $status = 'ok';
my $message;
if (defined($self->{instance_mode}->{option_results}->{'critical-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'critical-' . $self->{label}})) {
$status = 'critical';
} elsif (defined($self->{instance_mode}->{option_results}->{'warning-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'warning-' . $self->{label}})) {
$status = 'warning';
} elsif (defined($self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}}) && $self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}} ne '' &&
$self->eval(value => $self->{instance_mode}->{option_results}->{'unknown-' . $self->{label}})) {
$status = 'unknown';
}
return $status;
}
sub catalog_status_calc {
my ($self, %options) = @_;
foreach (keys %{$options{new_datas}}) {
if (/^\Q$self->{instance}\E_(.*)/) {
$self->{result_values}->{$1} = $options{new_datas}->{$_};
}
}
}
1;
CENTREON_PLUGINS_TEMPLATES_CATALOG_FUNCTIONS
$fatpacked{"centreon/plugins/templates/counter.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_COUNTER';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::templates::counter;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::values;
use centreon::plugins::misc;
use JSON::XS;
my $sort_subs = {
num => sub { $a <=> $b },
cmp => sub { $a cmp $b },
};
sub set_counters {
my ($self, %options) = @_;
if (!defined($self->{maps_counters})) {
$self->{maps_counters} = {};
}
$self->{maps_counters_type} = [];
# 0 = mode total
# 1 = mode instances
#push @{$self->{maps_counters_type}}, {
# name => 'global', type => 0, message_separator => ', ', cb_prefix_output => undef, cb_init => undef,
#};
#$self->{maps_counters}->{global} = [
# { label => 'client', set => {
# key_values => [ { name => 'client' } ],
# output_template => 'Current client connections : %s',
# perfdatas => [
# { label => 'Client', value => 'client', template => '%s',
# min => 0, unit => 'con' },
# ],
# }
# },
#];
# Example for instances
#push @{$self->{maps_counters_type}}, {
# name => 'cpu', type => 1, message_separator => ', ', cb_prefix_output => undef, cb_init => undef,
# message_multiple => 'All CPU usages are ok',
#};
}
sub get_callback {
my ($self, %options) = @_;
if (defined($options{method_name})) {
return $self->can($options{method_name});
}
return undef;
}
sub call_object_callback {
my ($self, %options) = @_;
if (defined($options{method_name})) {
my $method = $self->can($options{method_name});
if ($method) {
return $self->$method(%options);
}
}
return undef;
}
sub get_threshold_prefix {
my ($self, %options) = @_;
my $prefix = '';
END_LOOP: foreach (@{$self->{maps_counters_type}}) {
if ($_->{name} eq $options{name}) {
$prefix = 'instance-' if ($_->{type} == 1);
last;
}
if ($_->{type} == 3) {
foreach (@{$_->{group}}) {
if ($_->{name} eq $options{name}) {
$prefix = 'instance-' if ($_->{type} == 0);
$prefix = 'subinstance-' if ($_->{type} == 1);
last END_LOOP;
}
}
}
}
return $prefix;
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{version} = '1.0';
$options{options}->add_options(arguments => {
'filter-counters-block:s' => { name => 'filter_counters_block' },
'filter-counters:s' => { name => 'filter_counters' },
'display-ok-counters:s' => { name => 'display_ok_counters' },
'list-counters' => { name => 'list_counters' }
});
$self->{statefile_value} = undef;
if (defined($options{statefile}) && $options{statefile}) {
centreon::plugins::misc::mymodule_load(
output => $self->{output},
module => 'centreon::plugins::statefile',
error_msg => "Cannot load module 'centreon::plugins::statefile'."
);
$self->{statefile_value} = centreon::plugins::statefile->new(%options);
}
$self->{maps_counters} = {} if (!defined($self->{maps_counters}));
$self->set_counters(%options);
foreach my $key (keys %{$self->{maps_counters}}) {
foreach (@{$self->{maps_counters}->{$key}}) {
my $label = $_->{label};
my $thlabel = $label;
if ($self->{output}->use_new_perfdata() && defined($_->{nlabel})) {
$label = $_->{nlabel};
$thlabel = $self->get_threshold_prefix(name => $key) . $label;
}
$thlabel =~ s/\./-/g;
if (!defined($_->{threshold}) || $_->{threshold} != 0) {
$options{options}->add_options(arguments => {
'unknown-' . $thlabel . ':s' => { name => 'unknown-' . $thlabel, default => $_->{unknown_default} },
'warning-' . $thlabel . ':s' => { name => 'warning-' . $thlabel, default => $_->{warning_default} },
'critical-' . $thlabel . ':s' => { name => 'critical-' . $thlabel, default => $_->{critical_default} }
});
if (defined($_->{nlabel})) {
$options{options}->add_options(arguments => {
'unknown-' . $_->{label} . ':s' => { name => 'unknown-' . $_->{label}, redirect => 'unknown-' . $thlabel },
'warning-' . $_->{label} . ':s' => { name => 'warning-' . $_->{label}, redirect => 'warning-' . $thlabel },
'critical-' . $_->{label} . ':s' => { name => 'critical-' . $_->{label}, redirect => 'critical-' . $thlabel }
});
}
}
$_->{obj} = centreon::plugins::values->new(
statefile => $self->{statefile_value},
output => $self->{output}, perfdata => $self->{perfdata},
label => $_->{label}, nlabel => $_->{nlabel}, thlabel => $thlabel
);
$_->{obj}->set(%{$_->{set}});
}
}
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (defined($self->{option_results}->{list_counters})) {
my $list_counter = '';
my $th_counter = '';
my $counters;
foreach my $key (keys %{$self->{maps_counters}}) {
foreach (@{$self->{maps_counters}->{$key}}) {
$counters->{metrics}->{$_->{label}}->{nlabel} ="";
$counters->{metrics}->{$_->{label}}->{min}="";
$counters->{metrics}->{$_->{label}}->{max}="";
$counters->{metrics}->{$_->{label}}->{unit}="";
$counters->{metrics}->{$_->{label}}->{output_template}="";
if(defined($_->{nlabel})) {
$counters->{metrics}->{$_->{label}}->{nlabel} = $_->{nlabel};
}
if(defined($_->{set}->{perfdatas}->[0]->{min})) {
$counters->{metrics}->{$_->{label}}->{min} = $_->{set}->{perfdatas}->[0]->{min};
}
if(defined($_->{set}->{perfdatas}->[0]->{max})) {
$counters->{metrics}->{$_->{label}}->{max} = $_->{set}->{perfdatas}->[0]->{max};
}
if(defined($_->{set}->{perfdatas}->[0]->{unit})) {
$counters->{metrics}->{$_->{label}}->{unit} = $_->{set}->{perfdatas}->[0]->{unit};
}
if(defined($_->{set}->{perfdatas}->[0]->{template})) {
$counters->{metrics}->{$_->{label}}->{output_template} = $_->{set}->{perfdatas}->[0]->{template};
}
my $label = $_->{label};
$label =~ s/-//g;
$list_counter .= $_->{label}." ";
$th_counter .= " --warning-$_->{label}='\$_SERVICEWARNING" . uc($label) . "\$' --critical-$_->{label}='\$_SERVICECRITICAL" . uc($label) . "\$'";
}
}
$counters->{"counter list"}=$list_counter;
$counters->{"pack configuration"}=$th_counter." \$_SERVICEEXTRAOPTIONS\$";
my $result_data ="";
eval {
$result_data = JSON::XS->new->indent->space_after->canonical->utf8->encode($counters);
};
if ($@) {
$self->{output}->add_option_msg(short_msg => "Cannot use \$counters as it is a malformed JSON: " . $@);
$self->{output}->option_exit();
}
$self->{output}->output_add(short_msg => "counter list: ".$list_counter);
$self->{output}->output_add(long_msg => $result_data);
$self->{output}->display(nolabel => 1, force_ignore_perfdata => 1);
$self->{output}->exit();
}
my $change_macros_opt = [];
foreach my $key (keys %{$self->{maps_counters}}) {
foreach (@{$self->{maps_counters}->{$key}}) {
push @$change_macros_opt, 'unknown-' . $_->{label}, 'warning-' . $_->{label}, 'critical-' . $_->{label}
if (defined($_->{type}) && $_->{type} == 2);
$_->{obj}->{instance_mode} = $self;
$_->{obj}->init(option_results => $self->{option_results}) if (!defined($_->{type}) || $_->{type} != 2);
}
}
$self->change_macros(macros => $change_macros_opt) if (scalar(@$change_macros_opt) > 0);
if (defined($self->{statefile_value})) {
$self->{statefile_value}->check_options(%options);
}
}
sub run_global {
my ($self, %options) = @_;
return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne ''
&& $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
my $resume = defined($options{resume}) && $options{resume} == 1 ? 1 : 0;
my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
# Can be set when it comes from type 3 counters
my $called_multiple = defined($options{called_multiple}) && $options{called_multiple} == 1 ? 1 : 0;
my $multiple_parent = defined($options{multiple_parent}) && $options{multiple_parent} == 1 ? 1 : 0;
my $force_instance = defined($options{force_instance}) ? $options{force_instance} : undef;
my $message_separator = defined($options{config}->{message_separator}) ?
$options{config}->{message_separator}: ', ';
my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
my @exits;
foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
my $obj = $_->{obj};
next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
$_->{label} !~ /$self->{option_results}->{filter_counters}/);
$obj->set(instance => defined($force_instance) ? $force_instance : $options{config}->{name});
my ($value_check) = $obj->execute(new_datas => $self->{new_datas}, values => $self->{$options{config}->{name}});
next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
if ($value_check != 0) {
$long_msg .= $long_msg_append . $obj->output_error();
$long_msg_append = $message_separator;
next;
}
my $exit2 = $obj->threshold_check();
push @exits, $exit2;
my $output = $obj->output();
if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
(defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
$_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
$long_msg .= $long_msg_append . $output;
$long_msg_append = $message_separator;
}
if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
$short_msg .= $short_msg_append . $output;
$short_msg_append = $message_separator;
}
$obj->perfdata(extra_instance => $multiple_parent);
}
my ($prefix_output, $suffix_output);
$prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance_value => $self->{$options{config}->{name}})
if (defined($options{config}->{cb_prefix_output}));
$prefix_output = '' if (!defined($prefix_output));
$suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output}, instance_value => $self->{$options{config}->{name}})
if (defined($options{config}->{cb_suffix_output}));
$suffix_output = '' if (!defined($suffix_output));
if ($called_multiple == 1 && $long_msg ne '') {
$self->{output}->output_add(long_msg => $options{indent_long_output} . $prefix_output. $long_msg . $suffix_output);
}
my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
if ($called_multiple == 0) {
$self->{output}->output_add(
severity => $exit,
short_msg => $prefix_output . $short_msg . $suffix_output
);
} else {
$self->run_multiple_prefix_output(
severity => $exit,
short_msg => $prefix_output . $short_msg . $suffix_output
);
}
} else {
if ($long_msg ne '' && $multiple_parent == 0) {
if ($called_multiple == 0) {
$self->{output}->output_add(short_msg => $prefix_output . $long_msg . $suffix_output)
if ($display_short == 1);
} else {
$self->run_multiple_prefix_output(
severity => 'ok',
short_msg => $prefix_output . $long_msg . $suffix_output
) if ($display_short == 1);
}
}
}
}
sub run_instances {
my ($self, %options) = @_;
return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne ''
&& $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
my $cb_init_counters = $self->get_callback(method_name => $options{config}->{cb_init_counters});
my $display_status_lo = defined($options{display_status_long_output}) && $options{display_status_long_output} == 1 ? 1 : 0;
my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
my $display_long = (!defined($options{config}->{display_long}) || $options{config}->{display_long} != 0) ? 1 : 0;
my $resume = defined($options{resume}) && $options{resume} == 1 ? 1 : 0;
my $no_message_multiple = 1;
$self->{lproblems} = 0;
$self->{multiple} = 1;
if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
$self->{multiple} = 0;
}
my $message_separator = defined($options{config}->{message_separator}) ?
$options{config}->{message_separator}: ', ';
# The default sort method is cmp (string comparison)
my $sort_method = 'cmp';
# If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
$sort_method = $options{config}->{sort_method}
if (defined($options{config}->{sort_method}));
# In the absence of sort_attribute the sort method is set now
my $sort_sub = $sort_subs->{$sort_method};
# If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
if (defined($options{config}->{sort_attribute})) {
my $sort_attribute = $options{config}->{sort_attribute};
if ($sort_method eq 'cmp') {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
} else {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
}
}
# Now the loop begins with the desired sorting method
foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
my @exits = ();
foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
my $obj = $_->{obj};
next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
$_->{label} !~ /$self->{option_results}->{filter_counters}/);
next if ($cb_init_counters && $self->$cb_init_counters(%$_) == 1);
$no_message_multiple = 0;
$obj->set(instance => $id);
my ($value_check) = $obj->execute(
new_datas => $self->{new_datas},
values => $self->{$options{config}->{name}}->{$id}
);
next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
if ($value_check != 0) {
$long_msg .= $long_msg_append . $obj->output_error();
$long_msg_append = $message_separator;
next;
}
my $exit2 = $obj->threshold_check();
push @exits, $exit2;
my $output = $obj->output();
if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
(defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
$_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
$long_msg .= $long_msg_append . $output;
$long_msg_append = $message_separator;
}
if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
$self->{lproblems}++;
$short_msg .= $short_msg_append . $output;
$short_msg_append = $message_separator;
}
$obj->perfdata(extra_instance => $self->{multiple});
}
my ($prefix_output, $suffix_output);
$prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
if (defined($options{config}->{cb_prefix_output}));
$prefix_output = '' if (!defined($prefix_output));
$suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output})
if (defined($options{config}->{cb_suffix_output}));
$suffix_output = '' if (!defined($suffix_output));
my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
# in mode grouped, we don't display 'ok'
my $debug = 0;
$debug = 1 if ($display_status_lo == 1 && $self->{output}->is_status(value => $exit, compare => 'OK', litteral => 1));
if (scalar @{$self->{maps_counters}->{$options{config}->{name}}} > 0 && $long_msg ne '') {
$self->{output}->output_add(long_msg => ($display_status_lo == 1 ? lc($exit) . ': ' : '') . $prefix_output . $long_msg . $suffix_output, debug => $debug)
if ($display_long == 1);
}
if ($resume == 1) {
$self->{most_critical_instance} = $self->{output}->get_most_critical(status => [ $self->{most_critical_instance}, $exit ]);
next;
}
if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
$self->{output}->output_add(
severity => $exit,
short_msg => $prefix_output . $short_msg . $suffix_output
);
}
if ($self->{multiple} == 0) {
$self->{output}->output_add(short_msg => $prefix_output . $long_msg . $suffix_output)
if ($display_short == 1);
}
}
if ($no_message_multiple == 0 && $self->{multiple} == 1 && $resume == 0) {
$self->{output}->output_add(short_msg => $options{config}->{message_multiple})
if ($display_short == 1);
}
}
sub run_group {
my ($self, %options) = @_;
my $multiple = 1;
return if (scalar(keys %{$self->{$options{config}->{name}}}) <= 0);
if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
$multiple = 0;
}
if ($multiple == 1) {
$self->{output}->output_add(
severity => 'OK',
short_msg => $options{config}->{message_multiple}
);
}
my $format_output = defined($options{config}->{format_output}) ? $options{config}->{format_output} : '%s problem(s) detected';
my ($global_exit, $total_problems) = ([], 0);
foreach my $id (sort keys %{$self->{$options{config}->{name}}}) {
$self->{most_critical_instance} = 'ok';
if (defined($options{config}->{cb_long_output})) {
$self->{output}->output_add(
long_msg => $self->call_object_callback(
method_name => $options{config}->{cb_long_output},
instance => $id,
instance_value => $self->{$options{config}->{name}}->{$id}
)
);
}
foreach my $group (@{$options{config}->{group}}) {
$self->{$group->{name}} = $self->{$options{config}->{name}}->{$id}->{$group->{name}};
# we resume datas
$self->run_instances(config => $group, display_status_long_output => 1, resume => 1);
push @{$global_exit}, $self->{most_critical_instance};
$total_problems += $self->{lproblems};
my $prefix_output;
$prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
if (defined($options{config}->{cb_prefix_output}));
$prefix_output = '' if (!defined($prefix_output));
if ($multiple == 0 && (!defined($group->{display}) || $group->{display} != 0)) {
$self->{output}->output_add(
severity => $self->{most_critical_instance},
short_msg => sprintf("${prefix_output}" . $format_output, $self->{lproblems})
);
}
}
}
if ($multiple == 1) {
my $exit = $self->{output}->get_most_critical(status => [ @{$global_exit} ]);
if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf($format_output, $total_problems)
);
}
}
if (defined($options{config}->{display_counter_problem})) {
$self->{output}->perfdata_add(
label => $options{config}->{display_counter_problem}->{label},
nlabel => $options{config}->{display_counter_problem}->{nlabel},
unit => $options{config}->{display_counter_problem}->{unit},
value => $total_problems,
min => $options{config}->{display_counter_problem}->{min}, max => $options{config}->{display_counter_problem}->{max}
);
}
}
sub run_multiple_instances {
my ($self, %options) = @_;
return undef if (defined($self->{option_results}->{filter_counters_block}) && $self->{option_results}->{filter_counters_block} ne ''
&& $options{config}->{name} =~ /$self->{option_results}->{filter_counters_block}/);
return undef if (defined($options{config}->{cb_init}) && $self->call_object_callback(method_name => $options{config}->{cb_init}) == 1);
my $use_new_perfdata = $self->{output}->use_new_perfdata();
my $multiple_parent = defined($options{multiple_parent}) && $options{multiple_parent} == 1 ? $options{multiple_parent} : 0;
my $indent_long_output = defined($options{indent_long_output}) ? $options{indent_long_output} : '';
my $no_message_multiple = 1;
my $display_long = (!defined($options{config}->{display_long}) || $options{config}->{display_long} != 0) ? 1 : 0;
my $display_short = (!defined($options{config}->{display_short}) || $options{config}->{display_short} != 0) ? 1 : 0;
my $multiple = 1;
if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
$multiple = 0;
}
my $message_separator = defined($options{config}->{message_separator}) ?
$options{config}->{message_separator} : ', ';
# The default sort method is cmp (string comparison)
my $sort_method = 'cmp';
# If configured otherwise, we take it from the counter (only other method is 'num' for '<=>')
$sort_method = $options{config}->{sort_method}
if (defined($options{config}->{sort_method}));
# In the absence of sort_attribute the sort method is set now
my $sort_sub = $sort_subs->{$sort_method};
# If sort_attribute is set, then we'll redefine how things are sorted depending on the specified sort_method
if (defined($options{config}->{sort_attribute})) {
my $sort_attribute = $options{config}->{sort_attribute};
if ($sort_method eq 'cmp') {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} cmp $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
} else {
$sort_sub = sub { $self->{$options{config}->{name}}->{$a}->{$sort_attribute} <=> $self->{$options{config}->{name}}->{$b}->{$sort_attribute}};
}
}
# Now the loop begins with the desired sorting method
foreach my $id (sort { $sort_sub->() } keys %{$self->{$options{config}->{name}}}) {
my ($short_msg, $short_msg_append, $long_msg, $long_msg_append) = ('', '', '', '');
my @exits = ();
foreach (@{$self->{maps_counters}->{$options{config}->{name}}}) {
my $obj = $_->{obj};
next if (defined($self->{option_results}->{filter_counters}) && $self->{option_results}->{filter_counters} ne '' &&
$_->{label} !~ /$self->{option_results}->{filter_counters}/);
my $instance = $id;
if ($use_new_perfdata || ($multiple_parent == 1 && $multiple == 1)) {
$instance = $options{instance_parent} . ($self->{output}->get_instance_perfdata_separator()) . $id;
} elsif ($multiple_parent == 1 && $multiple == 0) {
$instance = $options{instance_parent};
}
$no_message_multiple = 0;
$obj->set(instance => $instance);
my ($value_check) = $obj->execute(
new_datas => $self->{new_datas},
values => $self->{$options{config}->{name}}->{$id}
);
next if (defined($options{config}->{skipped_code}) && defined($options{config}->{skipped_code}->{$value_check}));
if ($value_check != 0) {
$long_msg .= $long_msg_append . $obj->output_error();
$long_msg_append = $message_separator;
next;
}
my $exit2 = $obj->threshold_check();
push @exits, $exit2;
my $output = $obj->output();
if (!defined($_->{display_ok}) || $_->{display_ok} != 0 ||
(defined($self->{option_results}->{display_ok_counters}) && $self->{option_results}->{display_ok_counters} ne '' &&
$_->{label} =~ /$self->{option_results}->{display_ok_counters}/)) {
$long_msg .= $long_msg_append . $output;
$long_msg_append = $message_separator;
}
if (!$self->{output}->is_status(litteral => 1, value => $exit2, compare => 'ok')) {
$short_msg .= $short_msg_append . $output;
$short_msg_append = $message_separator;
}
if ($multiple_parent == 1 && $multiple == 0) {
$obj->perfdata(extra_instance => 1);
} else {
$obj->perfdata(extra_instance => $multiple);
}
}
my ($prefix_output, $suffix_output);
$prefix_output = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $id, instance_value => $self->{$options{config}->{name}}->{$id})
if (defined($options{config}->{cb_prefix_output}));
$prefix_output = '' if (!defined($prefix_output));
$suffix_output = $self->call_object_callback(method_name => $options{config}->{cb_suffix_output})
if (defined($options{config}->{cb_suffix_output}));
$suffix_output = '' if (!defined($suffix_output));
my $exit = $self->{output}->get_most_critical(status => [ @exits ]);
if (scalar @{$self->{maps_counters}->{$options{config}->{name}}} > 0 && $long_msg ne '') {
$self->{output}->output_add(long_msg => $indent_long_output . $prefix_output . $long_msg . $suffix_output)
if ($display_long == 1);
}
if (!$self->{output}->is_status(litteral => 1, value => $exit, compare => 'ok')) {
$self->run_multiple_prefix_output(
severity => $exit,
short_msg => $prefix_output . $short_msg . $suffix_output
);
}
if ($multiple == 0 && $multiple_parent == 0) {
$self->run_multiple_prefix_output(severity => 'ok', short_msg => $prefix_output . $long_msg . $suffix_output)
if ($display_short == 1);
}
}
if ($no_message_multiple == 0 && $multiple == 1 && $multiple_parent == 0) {
$self->run_multiple_prefix_output(severity => 'ok', short_msg => $options{config}->{message_multiple})
if ($display_short == 1);
}
}
sub run_multiple_prefix_output {
my ($self, %options) = @_;
my %separator;
if ($self->{prefix_multiple_output_done}->{lc($options{severity})} == 0) {
$self->{output}->output_add(severity => $options{severity}, short_msg => $self->{prefix_multiple_output});
$self->{prefix_multiple_output_done}->{lc($options{severity})} = 1;
$separator{separator} = '';
}
$self->{output}->output_add(severity => $options{severity}, short_msg => $options{short_msg}, %separator);
}
sub run_multiple {
my ($self, %options) = @_;
my $multiple = 1;
if (scalar(keys %{$self->{$options{config}->{name}}}) <= 1) {
$multiple = 0;
}
if ($multiple == 1) {
$self->{output}->output_add(
severity => 'OK',
short_msg => $options{config}->{message_multiple}
);
}
foreach my $instance (sort keys %{$self->{$options{config}->{name}}}) {
if (defined($options{config}->{cb_long_output})) {
$self->{output}->output_add(
long_msg => $self->call_object_callback(
method_name => $options{config}->{cb_long_output},
instance => $instance,
instance_value => $self->{$options{config}->{name}}->{$instance}
)
);
}
$self->{prefix_multiple_output} = '';
$self->{prefix_multiple_output_done} = { ok => 0, warning => 0, critical => 0, unknown => 0 };
$self->{prefix_multiple_output} = $self->call_object_callback(method_name => $options{config}->{cb_prefix_output}, instance => $instance, instance_value => $self->{$options{config}->{name}}->{$instance})
if (defined($options{config}->{cb_prefix_output}));
my $indent_long_output = '';
$indent_long_output = $options{config}->{indent_long_output}
if (defined($options{config}->{indent_long_output}));
foreach my $group (@{$options{config}->{group}}) {
next if (!defined($self->{$options{config}->{name}}->{$instance}->{$group->{name}}));
$self->{$group->{name}} = $self->{$options{config}->{name}}->{$instance}->{$group->{name}};
if ($group->{type} == 1) {
$self->run_multiple_instances(config => $group, multiple_parent => $multiple, instance_parent => $instance, indent_long_output => $indent_long_output);
} elsif ($group->{type} == 0) {
$self->run_global(
config => $group,
multiple_parent => $multiple,
called_multiple => 1,
force_instance => $instance,
indent_long_output => $indent_long_output
);
}
}
}
}
sub read_statefile_key {
my ($self, %options) = @_;
$self->{statefile_value}->read(statefile => $self->{cache_name});
return $self->{statefile_value}->get(name => $options{key});
}
sub set_timestamp {
my ($self, %options) = @_;
$self->{override_timestamp} = $options{timestamp};
}
sub run {
my ($self, %options) = @_;
$self->manage_selection(%options);
$self->{new_datas} = undef;
if (defined($self->{statefile_value})) {
$self->{new_datas} = {};
$self->{statefile_value}->read(statefile => $self->{cache_name}) if (defined($self->{cache_name}));
$self->{new_datas}->{last_timestamp} = defined($self->{override_timestamp}) ? $self->{override_timestamp} : time();
}
foreach my $entry (@{$self->{maps_counters_type}}) {
if ($entry->{type} == 0) {
$self->run_global(config => $entry);
} elsif ($entry->{type} == 1) {
$self->run_instances(config => $entry);
} elsif ($entry->{type} == 2) {
$self->run_group(config => $entry);
} elsif ($entry->{type} == 3) {
$self->run_multiple(config => $entry);
}
}
if (defined($self->{statefile_value})) {
$self->{statefile_value}->write(data => $self->{new_datas});
}
$self->{output}->display();
$self->{output}->exit();
}
sub manage_selection {
my ($self, %options) = @_;
# example for snmp
#use Digest::MD5 qw(md5_hex);
#$self->{cache_name} = "choose_name_" . $options{snmp}->get_hostname() . '_' . $options{snmp}->get_port() . '_' . $self->{mode} . '_' .
# (defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
}
sub compat_threshold_counter {
my ($self, %options) = @_;
foreach ('warning', 'critical') {
foreach my $th (@{$options{compat}->{th}}) {
next if (!defined($options{option_results}->{$_ . '-' . $th->[0]}) || $options{option_results}->{$_ . '-' . $th->[0]} eq '');
my $src_threshold = $options{option_results}->{$_ . '-' . $th->[0]};
if (defined($options{compat}->{units}) && $options{compat}->{units} eq '%') {
$options{option_results}->{$_ . '-' . $th->[0]} = undef;
if (defined($options{compat}->{free})) {
$options{option_results}->{$_ . '-' . $th->[0]} = undef;
my ($status, $result) = centreon::plugins::misc::parse_threshold(threshold => $src_threshold);
next if ($status == 0);
my $tmp = { arobase => $result->{arobase}, infinite_pos => 0, infinite_neg => 0, start => $result->{start}, end => $result->{end} };
$tmp->{infinite_neg} = 1 if ($result->{infinite_pos} == 1);
$tmp->{infinite_pos} = 1 if ($result->{infinite_neg} == 1);
if ($result->{start} ne '' && $result->{infinite_neg} == 0) {
$tmp->{end} = 100 - $result->{start};
}
if ($result->{end} ne '' && $result->{infinite_pos} == 0) {
$tmp->{start} = 100 - $result->{end};
}
$options{option_results}->{$_ . '-' . $th->[1]->{prct}} = centreon::plugins::misc::get_threshold_litteral(%$tmp);
} else {
$options{option_results}->{$_ . '-' . $th->[1]->{prct}} = $src_threshold;
}
} elsif (defined($options{compat}->{free})) {
$options{option_results}->{$_ . '-' . $th->[1]->{free}} = $options{option_results}->{$_ . '-' . $th->[0]};
$options{option_results}->{$_ . '-' . $th->[0]} = undef;
}
}
}
}
sub change_macros {
my ($self, %options) = @_;
foreach (@{$options{macros}}) {
if (defined($self->{option_results}->{$_}) && $self->{option_results}->{$_} ne '') {
$self->{option_results}->{$_} =~ s/%\{(.*?)\}/\$values->{$1}/g;
}
}
}
sub custom_perfdata_instances {
my ($self, %options) = @_;
my $instances = [];
foreach (split(/\s+/, $options{instances})) {
while (/%\((.+?)\)/g) {
my $name = $1;
if (!defined($options{labels}->{$name})) {
$self->{output}->add_option_msg(short_msg => "option $options{option_name} unsupported label: %($name)");
$self->{output}->option_exit();
}
push @$instances, $name;
}
}
if (scalar(@$instances) <= 0) {
$self->{output}->add_option_msg(short_msg => "option $options{option_name} need at least one label");
$self->{output}->option_exit();
}
return $instances;
}
1;
=head1 MODE
Default template for counters. Should be extended.
=over 8
=item B<--filter-counters>
Only display some counters (regexp can be used).
Example to check SSL connections only : --filter-counters='^xxxx|yyyy$'
=item B<--warning-*>
Warning threshold.
Can be: 'xxx', 'xxx'.
=item B<--critical-*>
Critical threshold.
Can be: 'xxx', 'xxx'.
=back
=cut
CENTREON_PLUGINS_TEMPLATES_COUNTER
$fatpacked{"centreon/plugins/templates/hardware.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_TEMPLATES_HARDWARE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::templates::hardware;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
sub set_system {
my ($self, %options) = @_;
#$self->{regexp_threshold_numeric_check_section_option} = '';
#$self->{cb_threshold_numeric_check_section_option} = 'callbackname';
# Some callbacks
#$self->{cb_hook1} = 'callbackname'; # before the loads
#$self->{cb_hook2} = 'callbackname'; # between loads and requests
#$self->{cb_hook3} = 'callbackname'; # after requests
#$self->{cb_hook4} = 'callbackname'; # after output
# Example for threshold:
#$self->{thresholds} = {
# fan => [
# ['bad', 'CRITICAL'],
# ['good', 'OK'],
# ['notPresent', 'OK'],
# ],
#};
# Unset the call to load components
#$self->{components_exec_load} = 0;
# Set the path_info
#$self->{components_path} = 'network::xxxx::mode::components';
# Set the components
#$self->{components_module} = ['cpu', 'memory', ...];
}
sub call_object_callback {
my ($self, %options) = @_;
if (defined($options{method_name})) {
my $method = $self->can($options{method_name});
if ($method) {
return $self->$method(%options);
}
}
return undef;
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{version} = '1.0';
$options{options}->add_options(arguments => {
'component:s' => { name => 'component', default => '.*' },
'no-component:s' => { name => 'no_component', default => 'critical' },
'threshold-overload:s@' => { name => 'threshold_overload' },
'add-name-instance' => { name => 'add_name_instance' },
'no-component-count' => { name => 'no_component_count' }
});
$self->{performance} = (defined($options{no_performance}) && $options{no_performance} == 1) ?
0 : 1;
if ($self->{performance} == 1) {
$options{options}->add_options(arguments => {
'warning:s@' => { name => 'warning' },
'critical:s@' => { name => 'critical' }
});
}
$self->{filter_exclude} = (defined($options{no_filter_exclude}) && $options{no_filter_exclude} == 1) ?
0 : 1;
if ($self->{filter_exclude} == 1) {
$options{options}->add_options(arguments => {
'exclude:s' => { name => 'exclude' },
'filter:s@' => { name => 'filter' }
});
}
$self->{absent} = (defined($options{no_absent}) && $options{no_absent} == 1) ?
0 : 1;
if ($self->{absent} == 1) {
$options{options}->add_options(arguments => {
'absent-problem:s@' => { name => 'absent_problem' }
});
}
$self->{load_components} = (defined($options{no_load_components}) && $options{no_load_components} == 1) ?
0 : 1;
$self->{components} = {};
$self->{no_components} = undef;
$self->{components_module} = [];
$self->{components_exec_load} = 1;
$self->set_system();
$self->{count} = (defined($options{no_count}) && $options{no_count} == 1) ? 0 : 1;
if ($self->{count} == 1) {
foreach my $component (@{$self->{components_module}}) {
$options{options}->add_options(arguments => {
'unknown-count-' . $component . ':s' => { name => 'unknown_count_' . $component },
'warning-count-' . $component . ':s' => { name => 'warning_count_' . $component },
'critical-count-' . $component . ':s' => { name => 'critical_count_' . $component }
});
}
}
$self->{request} = [];
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
# For compatibility both $self->{option_results}->{no_component} and $self->{no_components} are initialized.
# If unset or empty the default value 'critical' is used.
$self->{option_results}->{no_component} = $self->{no_components} = $self->{option_results}->{no_component} || 'critical';
if ($self->{filter_exclude} == 1) {
$self->{filter} = [];
foreach my $val (@{$self->{option_results}->{filter}}) {
next if (!defined($val) || $val eq '');
my @values = split (/,/, $val);
push @{$self->{filter}}, { filter => $values[0], instance => $values[1] };
}
}
if ($self->{absent} == 1) {
$self->{absent_problem} = [];
foreach my $val (@{$self->{option_results}->{absent_problem}}) {
next if (!defined($val) || $val eq '');
my @values = split (/,/, $val);
push @{$self->{absent_problem}}, { filter => $values[0], instance => $values[1] };
}
}
$self->{overload_th} = [];
foreach my $val (@{$self->{option_results}->{threshold_overload}}) {
next if (!defined($val) || $val eq '');
my @values = split (/,/, $val);
if (scalar(@values) < 3) {
$self->{output}->add_option_msg(short_msg => "Wrong threshold-overload option '" . $val . "'.");
$self->{output}->option_exit();
}
my ($section, $instance, $status, $filter);
if (scalar(@values) == 3) {
($section, $status, $filter) = @values;
$instance = '.*';
} else {
($section, $instance, $status, $filter) = @values;
}
if ($self->{output}->is_litteral_status(status => $status) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong threshold-overload status '" . $val . "'.");
$self->{output}->option_exit();
}
push @{$self->{overload_th}}, { section => $section, filter => $filter, status => $status, instance => $instance };
}
if ($self->{performance} == 1) {
$self->{numeric_threshold} = {};
foreach my $option (('warning', 'critical')) {
foreach my $val (@{$self->{option_results}->{$option}}) {
next if (!defined($val) || $val eq '');
if ($val !~ /^(.*?),(.*?),(.*)$/) {
$self->{output}->add_option_msg(short_msg => "Wrong $option option '" . $val . "'.");
$self->{output}->option_exit();
}
my ($section, $instance, $value) = ($1, $2, $3);
if (defined($self->{regexp_threshold_numeric_check_section_option}) &&
$section !~ /$self->{regexp_threshold_numeric_check_section_option}/) {
$self->{output}->add_option_msg(short_msg => "Wrong $option option '" . $val . "'.");
$self->{output}->option_exit();
}
$self->call_object_callback(
method_name => $self->{cb_threshold_numeric_check_section_option},
section => $section,
option_name => $option,
option_value => $val
);
my $position = 0;
if (defined($self->{numeric_threshold}->{$section})) {
$position = scalar(@{$self->{numeric_threshold}->{$section}});
}
if (($self->{perfdata}->threshold_validate(label => $option . '-' . $section . '-' . $position, value => $value)) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong $option threshold '" . $value . "'.");
$self->{output}->option_exit();
}
$self->{numeric_threshold}->{$section} = [] if (!defined($self->{numeric_threshold}->{$section}));
push @{$self->{numeric_threshold}->{$section}}, { label => $option . '-' . $section . '-' . $position, threshold => $option, instance => $instance };
}
}
}
if ($self->{count} == 1) {
foreach my $comp (@{$self->{components_module}}) {
foreach my $threshold (('warning', 'critical', 'unknown')) {
if (($self->{perfdata}->threshold_validate(label => $threshold . '-count-' . $comp, value => $self->{option_results}->{$threshold . '_count_' . $comp})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong " . $threshold . " threshold '" . $self->{option_results}->{$threshold . '_count_' . $comp} . "'.");
$self->{output}->option_exit();
}
}
}
}
}
sub load_components {
my ($self, %options) = @_;
foreach (@{$self->{components_module}}) {
if (/$self->{option_results}->{component}/) {
my $mod_name = $self->{components_path} . "::$_";
centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => $mod_name,
error_msg => "Cannot load module '$mod_name'.") if ($self->{load_components} == 1);
$self->{loaded} = 1;
if ($self->{components_exec_load} == 1) {
my $func = $mod_name->can('load');
$func->($self);
}
}
}
}
sub exec_components {
my ($self, %options) = @_;
foreach (@{$self->{components_module}}) {
if (/$self->{option_results}->{component}/) {
my $mod_name = $self->{components_path} . "::$_";
my $func = $mod_name->can('check');
$func->($self);
}
}
}
sub display {
my ($self, %options) = @_;
my $total_components = 0;
my $display_by_component = '';
my $display_by_component_append = '';
my $exit = 'OK';
my $exits = [];
my ($warn, $crit);
foreach my $comp (sort(keys %{$self->{components}})) {
# Skipping short msg when no components
next if (!defined($self->{option_results}->{no_component_count}) && $self->{components}->{$comp}->{total} == 0 && $self->{components}->{$comp}->{skip} == 0);
next if (defined($self->{option_results}->{component}) && $comp !~ /$self->{option_results}->{component}/ );
if ($self->{count} == 1) {
($exit, $warn, $crit) = $self->get_severity_count(label => $comp, value => $self->{components}->{$comp}->{total});
if (!$self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
severity => $exit,
short_msg => sprintf(
"'%s' components '%s' checked",
$self->{components}->{$comp}->{total},
$comp
)
);
}
$self->{output}->perfdata_add(
label => 'count_' . $comp,
nlabel => 'hardware.' . $comp . '.count',
value => $self->{components}->{$comp}->{total},
warning => $warn,
critical => $crit
);
push @{$exits}, $exit;
}
$total_components += $self->{components}->{$comp}->{total} + $self->{components}->{$comp}->{skip};
my $count_by_components = $self->{components}->{$comp}->{total} + $self->{components}->{$comp}->{skip};
$display_by_component .= $display_by_component_append . $self->{components}->{$comp}->{total} . '/' . $count_by_components . ' ' . $self->{components}->{$comp}->{name};
$display_by_component_append = ', ';
}
$exit = $self->{output}->get_most_critical(status => $exits) if (scalar(@{$exits}) > 0);
if ($self->{output}->is_status(value => $exit, compare => 'ok', litteral => 1)) {
$self->{output}->output_add(
short_msg => sprintf(
'All %s components are ok [%s].',
$total_components,
$display_by_component
)
);
}
if (defined($self->{option_results}->{no_component}) && $total_components == 0) {
$self->{output}->output_add(
severity => $self->{no_components},
short_msg => 'No components are checked.'
);
}
}
sub run {
my ($self, %options) = @_;
$self->{loaded} = 0;
$self->call_object_callback(method_name => $self->{cb_hook1}, %options);
$self->load_components(%options);
if ($self->{loaded} == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong option. Cannot find component '" . $self->{option_results}->{component} . "'.");
$self->{output}->option_exit();
}
$self->call_object_callback(method_name => $self->{cb_hook2}, %options);
$self->exec_components(%options);
$self->call_object_callback(method_name => $self->{cb_hook3}, %options);
$self->display();
$self->call_object_callback(method_name => $self->{cb_hook4}, %options);
$self->{output}->display();
$self->{output}->exit();
}
sub disco_format {
my ($self, %options) = @_;
$self->{output}->add_disco_format(elements => ['component', 'instance', 'description']);
}
sub disco_show {
my ($self, %options) = @_;
$self->{loaded} = 0;
$self->call_object_callback(method_name => $self->{cb_hook1}, %options);
$self->load_components(%options);
if ($self->{loaded} == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong option. Cannot find component '" . $self->{option_results}->{component} . "'.");
$self->{output}->option_exit();
}
$self->call_object_callback(method_name => $self->{cb_hook2}, %options);
foreach (@{$self->{components_module}}) {
if (/$self->{option_results}->{component}/) {
my $mod_name = $self->{components_path} . "::$_";
if (my $func = $mod_name->can('disco_show')) {
$func->($self);
}
}
}
$self->call_object_callback(method_name => $self->{cb_hook3}, %options);
}
sub check_filter {
my ($self, %options) = @_;
# Old compatibility variable. We'll be deleted
if (defined($self->{option_results}->{exclude})) {
if (defined($options{instance})) {
if ($self->{option_results}->{exclude} =~ /(^|\s|,)${options{section}}[^,]*#\Q$options{instance}\E#/) {
$self->{components}->{$options{section}}->{skip}++;
$self->{output}->output_add(long_msg => sprintf("skipping $options{section} section $options{instance} instance."));
return 1;
}
} elsif (defined($self->{option_results}->{exclude}) && $self->{option_results}->{exclude} =~ /(^|\s|,)$options{section}(\s|,|$)/) {
$self->{output}->output_add(long_msg => sprintf("skipping $options{section} section."));
return 1;
}
}
$options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
foreach (@{$self->{filter}}) {
if ($options{section} =~ /$_->{filter}/) {
if (!defined($options{instance}) && !defined($_->{instance})) {
$self->{output}->output_add(long_msg => sprintf("skipping $options{section} section."));
return 1;
} elsif (defined($options{instance}) && $options{instance} =~ /$_->{instance}/) {
$self->{output}->output_add(long_msg => sprintf("skipping $options{section} section $options{instance} instance."));
return 1;
}
}
}
return 0;
}
sub absent_problem {
my ($self, %options) = @_;
$options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
foreach (@{$self->{absent_problem}}) {
if ($options{section} =~ /$_->{filter}/) {
if (!defined($_->{instance}) || $options{instance} =~ /$_->{instance}/) {
$self->{output}->output_add(
severity => 'CRITICAL',
short_msg => sprintf(
"Component '%s' instance '%s' is not present",
$options{section},
$options{instance}
)
);
$self->{output}->output_add(long_msg => sprintf("Skipping $options{section} section $options{instance} instance (not present)"));
$self->{components}->{$options{section}}->{skip}++;
return 1;
}
}
}
return 0;
}
sub get_severity_count {
my ($self, %options) = @_;
my $status = 'OK'; # default
my $thresholds = { warning => undef, critical => undef };
$status = $self->{perfdata}->threshold_check(
value => $options{value},
threshold => [
{ label => 'critical-count-' . $options{label}, exit_litteral => 'critical' },
{ label => 'warning-count-' . $options{label}, exit_litteral => 'warning' },
{ label => 'unknown-count-' . $options{label}, exit_litteral => 'unknown' },
]
);
$thresholds->{critical} = $self->{perfdata}->get_perfdata_for_output(label => 'critical-count-' . $options{label});
$thresholds->{warning} = $self->{perfdata}->get_perfdata_for_output(label => 'warning-count-' . $options{label});
return ($status, $thresholds->{warning}, $thresholds->{critical});
}
sub get_severity_numeric {
my ($self, %options) = @_;
my $status = 'OK'; # default
my $thresholds = { warning => undef, critical => undef };
my $checked = 0;
$options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
if (defined($self->{numeric_threshold}->{$options{section}})) {
my $exits = [];
foreach (@{$self->{numeric_threshold}->{$options{section}}}) {
if ($options{instance} =~ /$_->{instance}/) {
push @{$exits}, $self->{perfdata}->threshold_check(value => $options{value}, threshold => [ { label => $_->{label}, exit_litteral => $_->{threshold} } ]);
$thresholds->{$_->{threshold}} = $self->{perfdata}->get_perfdata_for_output(label => $_->{label});
$checked = 1;
}
}
$status = $self->{output}->get_most_critical(status => $exits) if (scalar(@{$exits}) > 0);
}
return ($status, $thresholds->{warning}, $thresholds->{critical}, $checked);
}
sub get_severity {
my ($self, %options) = @_;
my $status = 'UNKNOWN'; # default
$options{instance} .= '#' . $options{name} if (defined($self->{option_results}->{add_name_instance}) && defined($options{name}));
foreach (@{$self->{overload_th}}) {
if ($options{section} =~ /$_->{section}/i) {
if ($options{value} =~ /$_->{filter}/i &&
(!defined($options{instance}) || $options{instance} =~ /$_->{instance}/)) {
$status = $_->{status};
return $status;
}
}
}
my $label = defined($options{label}) ? $options{label} : $options{section};
foreach (@{$self->{thresholds}->{$label}}) {
if ($options{value} =~ /$$_[0]/i) {
$status = $$_[1];
return $status;
}
}
return $status;
}
1;
=head1 MODE
Default template for hardware. Should be extended.
=over 8
=item B<--component>
Define which component should be monitored based on their names.
This option will be treated as a regular expression.
All components are included by default ('.*').
=item B<--filter>
Exclude some components. This option can be called several times (example: --filter=component1 --filter=component2).
You can also exclude components from a specific instance (example: --filter=component_name,instance_value).
=item B<--absent-problem>
Return an error if a component is not 'present' (default is skipping).
It can be set globally or for a specific instance: --absent-problem='component_name' or --absent-problem='component_name,instance_value'.
=item B<--no-component>
Define the expected status if no components are found (default: critical).
=item B<--threshold-overload>
Use this option to override the status returned by the plugin when the status label matches a regular expression (syntax: section,[instance,]status,regexp).
Example: --threshold-overload='xxxxx,CRITICAL,^(?!(normal)$)'
=item B<--warning>
Define the warning threshold for temperatures (syntax: type,instance,threshold)
Example: --warning='temperature,.*,30'
=item B<--critical>
Define the critical threshold for temperatures (syntax: type,instance,threshold)
Example: --critical='temperature,.*,40'
=item B<--warning-count-*>
Define the warning threshold for the number of components of one type (replace '*' with the component type).
=item B<--critical-count-*>
Define the critical threshold for the number of components of one type (replace '*' with the component type).
=back
=cut
CENTREON_PLUGINS_TEMPLATES_HARDWARE
$fatpacked{"centreon/plugins/values.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'CENTREON_PLUGINS_VALUES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package centreon::plugins::values;
use strict;
use warnings;
use centreon::plugins::misc;
# Warning message with sprintf and too much arguments.
# Really annoying. Need to disable that warning
no if ($^V gt v5.22.0), 'warnings' => 'redundant';
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
$self->{statefile} = $options{statefile};
$self->{output} = $options{output};
$self->{perfdata} = $options{perfdata};
$self->{label} = $options{label};
$self->{nlabel} = $options{nlabel};
$self->{thlabel} = defined($options{thlabel}) ? $options{thlabel} : $self->{label};
$self->{perfdatas} = [];
$self->{output_template} = $self->{label} . ' : %s';
$self->{output_use} = undef;
$self->{output_change_bytes} = 0;
$self->{output_error_template} = $self->{label} . ' : %s';
$self->{threshold_use} = undef;
$self->{threshold_warn} = undef;
$self->{threshold_crit} = undef;
$self->{per_second} = 0;
$self->{manual_keys} = 0;
$self->{last_timestamp} = undef;
$self->{result_values} = {};
$self->{safe_test} = 0;
return $self;
}
sub init {
my ($self, %options) = @_;
my $unkn = defined($self->{threshold_unkn}) ? $self->{threshold_unkn} : 'unknown-' . $self->{thlabel};
my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel};
if (($self->{perfdata}->threshold_validate(label => $unkn, value => $options{option_results}->{$unkn})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong $unkn threshold '" . $options{option_results}->{$unkn} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => $warn, value => $options{option_results}->{$warn})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong $warn threshold '" . $options{option_results}->{$warn} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => $crit, value => $options{option_results}->{$crit})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong $crit threshold '" . $options{option_results}->{$crit} . "'.");
$self->{output}->option_exit();
}
}
sub set {
my ($self, %options) = @_;
foreach (keys %options) {
$self->{$_} = $options{$_};
}
}
sub calc {
my ($self, %options) = @_;
# manage only one value ;)
foreach my $value (@{$self->{key_values}}) {
if (defined($value->{diff}) && $value->{diff} == 1) {
$self->{result_values}->{$value->{name}} = $options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}};
} elsif (defined($value->{per_second}) && $value->{per_second} == 1) {
$self->{result_values}->{$value->{name}} = ($options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}}) / $options{delta_time};
} elsif (defined($value->{per_minute}) && $value->{per_minute} == 1) {
$self->{result_values}->{$value->{name}} = ($options{new_datas}->{$self->{instance} . '_' . $value->{name}} - $options{old_datas}->{$self->{instance} . '_' . $value->{name}}) / ($options{delta_time} / 60);
} else {
$self->{result_values}->{$value->{name}} = $options{new_datas}->{$self->{instance} . '_' . $value->{name}};
}
}
return 0;
}
sub threshold_check {
my ($self, %options) = @_;
if (defined($self->{closure_custom_threshold_check})) {
return &{$self->{closure_custom_threshold_check}}($self, %options);
}
my $unkn = defined($self->{threshold_unkn}) ? $self->{threshold_unkn} : 'unknown-' . $self->{thlabel};
my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel};
my $value = '';
if (defined($self->{threshold_use})) {
$value = $self->{result_values}->{ $self->{threshold_use} };
} else {
$value = defined($self->{key_values}->[0]) ? $self->{result_values}->{ $self->{key_values}->[0]->{name} } : '';
}
return $self->{perfdata}->threshold_check(
value => $value, threshold => [
{ label => $crit, exit_litteral => 'critical' },
{ label => $warn, exit_litteral => 'warning' },
{ label => $unkn, exit_litteral => 'unknown' }
]
);
}
sub output_error {
my ($self, %options) = @_;
return sprintf($self->{output_error_template}, $self->{error_msg});
}
sub output {
my ($self, %options) = @_;
if (defined($self->{closure_custom_output})) {
return $self->{closure_custom_output}->($self);
}
my ($value, $unit, $name) = ('', '');
if (defined($self->{output_use})) {
$name = $self->{output_use};
} else {
$name = defined($self->{key_values}->[0]) ? $self->{key_values}->[0]->{name} : undef;
}
if (defined($name)) {
$value = $self->{result_values}->{$name};
if ($self->{output_change_bytes} == 1) {
($value, $unit) = $self->{perfdata}->change_bytes(value => $value);
} elsif ($self->{output_change_bytes} == 2) {
($value, $unit) = $self->{perfdata}->change_bytes(value => $value, network => 1);
}
}
return sprintf($self->{output_template}, $value, $unit);
}
sub use_instances {
my ($self, %options) = @_;
if (!defined($options{extra_instance}) || $options{extra_instance} != 0 || $self->{output}->use_new_perfdata()) {
return 1;
}
return 0;
}
sub perfdata {
my ($self, %options) = @_;
if (defined($self->{closure_custom_perfdata})) {
return &{$self->{closure_custom_perfdata}}($self, %options);
}
my $warn = defined($self->{threshold_warn}) ? $self->{threshold_warn} : 'warning-' . $self->{thlabel};
my $crit = defined($self->{threshold_crit}) ? $self->{threshold_crit} : 'critical-' . $self->{thlabel};
foreach my $perf (@{$self->{perfdatas}}) {
my ($label, $extra_label, $min, $max, $th_total) = ($self->{label}, '');
my $cast_int = (defined($perf->{cast_int}) && $perf->{cast_int} == 1) ? 1 : 0;
my $template = '%s';
$template = $perf->{template} if (defined($perf->{template}));
$label = $perf->{label} if (defined($perf->{label}));
if (defined($perf->{min})) {
$min = ($perf->{min} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{min}} : $perf->{min};
}
if (defined($perf->{max})) {
$max = ($perf->{max} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{max}} : $perf->{max};
}
if (defined($perf->{threshold_total})) {
$th_total = ($perf->{threshold_total} =~ /[^0-9.-]/) ? $self->{result_values}->{$perf->{threshold_total}} : $perf->{threshold_total};
}
my $instances;
if (defined($perf->{label_extra_instance}) && $perf->{label_extra_instance} == 1) {
my $instance = '';
if (defined($perf->{instance_use})) {
$instance = $self->{result_values}->{$perf->{instance_use}};
} else {
$instance = $self->{instance};
}
if (!defined($options{extra_instance}) || $options{extra_instance} != 0 || $self->{output}->use_new_perfdata()) {
$instances = $instance;
}
}
my $value = defined($perf->{value}) ? $perf->{value} : $self->{key_values}->[0]->{name};
$self->{output}->perfdata_add(
label => $label,
instances => $instances,
nlabel => $self->{nlabel},
unit => $perf->{unit},
value => $cast_int == 1 ? int($self->{result_values}->{$value}) : sprintf($template, $self->{result_values}->{$value}),
warning => $self->{perfdata}->get_perfdata_for_output(label => $warn, total => $th_total, cast_int => $cast_int),
critical => $self->{perfdata}->get_perfdata_for_output(label => $crit, total => $th_total, cast_int => $cast_int),
min => $min,
max => $max
);
}
}
sub eval {
my ($self, %options) = @_;
if ($self->{safe_test} == 0) {
my ($code) = centreon::plugins::misc::mymodule_load(
output => $self->{output}, module => 'Safe',
no_quit => 1
);
if ($code == 0) {
$self->{safe} = Safe->new();
$self->{safe}->share('$values');
}
$self->{safe_test} = 1;
}
my $result;
if (defined($self->{safe})) {
our $values = $self->{result_values};
$result = $self->{safe}->reval($options{value}, 1);
if ($@) {
die 'Unsafe code evaluation: ' . $@;
}
} else {
my $values = $self->{result_values};
{
local $SIG{__WARN__} = sub {}; # ignore
$result = eval "$options{value}";
if ($@) {
die 'Code evaluation error: ' . $@;
}
}
}
return $result;
}
sub execute {
my ($self, %options) = @_;
my $old_datas = {};
$self->{result_values} = {},
$self->{error_msg} = undef;
my $quit = 0;
$options{new_datas} = {} if (!defined($options{new_datas}));
foreach my $value (@{$self->{key_values}}) {
if (!defined($options{values}->{$value->{name}}) ||
defined($value->{no_value}) && $options{values}->{$value->{name}} eq $value->{no_value}) {
$quit = 2;
last;
}
if ((defined($value->{diff}) && $value->{diff} == 1) ||
(defined($value->{per_minute}) && $value->{per_minute} == 1) ||
(defined($value->{per_second}) && $value->{per_second} == 1)) {
$options{new_datas}->{$self->{instance} . '_' . $value->{name}} = $options{values}->{$value->{name}};
$old_datas->{$self->{instance} . '_' . $value->{name}} = $self->{statefile}->get(name => $self->{instance} . '_' . $value->{name});
if (!defined($old_datas->{$self->{instance} . '_' . $value->{name}})) {
$quit = 1;
next;
}
if ($old_datas->{$self->{instance} . '_' . $value->{name}} > $options{new_datas}->{$self->{instance} . '_' . $value->{name}}) {
$old_datas->{$self->{instance} . '_' . $value->{name}} = 0;
}
} else {
$options{new_datas}->{$self->{instance} . '_' . $value->{name}} = $options{values}->{$value->{name}};
if (defined($self->{statefile})) {
$old_datas->{$self->{instance} . '_' . $value->{name}} = $self->{statefile}->get(name => $self->{instance} . '_' . $value->{name});
}
}
}
# Very manual
if ($self->{manual_keys} == 1) {
foreach my $name (keys %{$options{values}}) {
$options{new_datas}->{$self->{instance} . '_' . $name} = $options{values}->{$name};
if (defined($self->{statefile})) {
$old_datas->{$self->{instance} . '_' . $name} = $self->{statefile}->get(name => $self->{instance} . '_' . $name);
}
}
}
if ($quit == 2) {
$self->{error_msg} = 'skipped (no value(s))';
return -10;
}
if (defined($self->{statefile})) {
$self->{last_timestamp} = $self->{statefile}->get(name => 'last_timestamp');
}
if ($quit == 1) {
$self->{error_msg} = 'Buffer creation';
return -1;
}
my $delta_time;
if (defined($self->{statefile}) && defined($self->{last_timestamp})) {
$delta_time = $options{new_datas}->{last_timestamp} - $self->{last_timestamp};
if ($delta_time <= 0) {
$delta_time = 1;
}
}
if (defined($self->{closure_custom_calc})) {
return $self->{closure_custom_calc}->(
$self,
old_datas => $old_datas,
new_datas => $options{new_datas},
delta_time => $delta_time,
extra_options => $self->{closure_custom_calc_extra_options}
);
}
return $self->calc(old_datas => $old_datas, new_datas => $options{new_datas}, delta_time => $delta_time);
}
1;
CENTREON_PLUGINS_VALUES
$fatpacked{"database/mysql/dbi.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_DBI';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::dbi;
use base qw(centreon::plugins::dbi);
use strict;
use warnings;
sub is_mariadb {
my ($self) = @_;
return $self->{is_mariadb};
}
sub set_version {
my ($self) = @_;
$self->{is_mariadb} = 0;
$self->{version} = $self->{instance}->get_info(18); # SQL_DBMS_VER
# MariaDB: 5.5.5-10.1.36-MariaDB, 10.1.36-MariaDB or 11.4.4-2-MariaDB-enterprise-log
if ($self->{version} =~ /(?:\d\.\d\.\d-)?(\d+\.\d+\.\d+).*MariaDB/i) {
$self->{is_mariadb} = 1;
}
}
1;
DATABASE_MYSQL_DBI
$fatpacked{"database/mysql/mode/backup.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_BACKUP';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::backup;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_status_output {
my ($self, %options) = @_;
my $msg;
if ($self->{result_values}->{has_backup} eq 'no') {
$msg = 'never executed';
} else {
$msg = sprintf(
'exit state: %s [last_error: %s]',
$self->{result_values}->{exit_state},
$self->{result_values}->{last_error}
);
}
return $msg;
}
sub prefix_backup_output {
my ($self, %options) = @_;
return "Backup '" . $options{instance_value}->{type} . "' ";
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'backups', type => 1, cb_prefix_output => 'prefix_backup_output', message_multiple => 'All backup types are ok', skipped_code => { -10 => 1 } },
];
$self->{maps_counters}->{backups} = [
{
label => 'status',
type => 2,
critical_default => '%{has_backup} eq "yes" and %{exit_state} ne "SUCCESS" and %{last_error} ne "NO_ERROR"',
set => {
key_values => [
{ name => 'type' }, { name => 'exit_state' },
{ name => 'last_error' }, { name => 'has_backup' }
],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
},
{ label => 'time-last-execution', nlabel => 'backup.time.last.execution.seconds', set => {
key_values => [ { name => 'last_execution_time' }, { name => 'last_execution_human' } ],
output_template => 'last execution time: %s',
output_use => 'last_execution_human',
perfdatas => [
{ template => '%d', min => 0, unit => 's', label_extra_instance => 1 }
]
}
}
];
}
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-type:s' => { name => 'filter_type' }
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
my $query = q{
SELECT
backup_type,
exit_state,
last_error,
UNIX_TIMESTAMP(start_time)
FROM mysql.backup_history
WHERE
backup_id IN (
SELECT MAX(backup_id)
FROM mysql.backup_history
GROUP BY backup_type
)
};
$options{sql}->query(query => $query);
my $result = $options{sql}->fetchall_arrayref();
$self->{backups} = {
FULL => { type => 'FULL', has_backup => 'no' , exit_state => '-', last_error => '-' },
PARTIAL => { type => 'PARTIAL', has_backup => 'no' , exit_state => '-', last_error => '-' },
DIFFERENTIAL => { type => 'DIFFERENTIAL', has_backup => 'no' , exit_state => '-', last_error => '-' },
INCREMENTAL => { type => 'INCREMENTAL', has_backup => 'no' , exit_state => '-', last_error => '-' },
TTS => { type => 'TTS', has_backup => 'no', exit_state => '-', last_error => '-' }
};
foreach my $row (@$result) {
my ($name, $state, $type, $total_mb, $usable_file_mb, $offline_disks, $free_mb) = @$row;
if (defined($self->{option_results}->{filter_type}) && $self->{option_results}->{filter_type} ne '' &&
$row->[0] !~ /$self->{option_results}->{filter_type}/) {
$self->{output}->output_add(long_msg => "skipping '" . $row->[0] . "': no matching filter name.", debug => 1);
next;
}
$self->{backups}->{ $row->[0] }->{has_backup} = 'yes';
$self->{backups}->{ $row->[0] }->{exit_state} = $row->[1];
$self->{backups}->{ $row->[0] }->{last_error} = $row->[2];
$self->{backups}->{ $row->[0] }->{last_execution_time} = time() - $row->[3];
$self->{backups}->{ $row->[0] }->{last_execution_human} = centreon::plugins::misc::change_seconds(
value => $self->{backups}->{ $row->[0] }->{last_execution_time}
);
}
}
1;
=head1 MODE
Check backups (only with mysql enterprise backup).
=over 8
=item B<--filter-type>
Filter backups by type (regexp can be used).
=item B<--unknown-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{has_backup}, %{last_error}, %{exit_state}, %{type}
=item B<--warning-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{has_backup}, %{last_error}, %{exit_state}, %{type}
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{has_backup} eq "yes" and %{exit_state} ne "SUCCESS" and %{last_error} ne "NO_ERROR"').
You can use the following variables: %{has_backup}, %{last_error}, %{exit_state}, %{type}
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'time-last-execution'.
=back
=cut
DATABASE_MYSQL_MODE_BACKUP
$fatpacked{"database/mysql/mode/databasessize.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_DATABASESSIZE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::databasessize;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_global_output', cb_init => 'skip_global' },
{ name => 'database', type => 3, cb_prefix_output => 'prefix_database_output',
cb_long_output => 'database_long_output', indent_long_output => ' ',
message_multiple => 'All databases are ok',
group => [
{ name => 'global_db', type => 0, skipped_code => { -10 => 1 } },
{ name => 'table', display_long => 0, cb_prefix_output => 'prefix_table_output',
message_multiple => 'All tables are ok', type => 1, skipped_code => { -10 => 1 } }
]
}
];
$self->{maps_counters}->{global} = [
{ label => 'total-usage', nlabel => 'databases.space.usage.bytes', set => {
key_values => [ { name => 'used' } ],
output_template => 'Used Space: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0 }
]
}
},
{ label => 'total-free', nlabel => 'databases.space.free.bytes', set => {
key_values => [ { name => 'free' } ],
output_template => 'Free Space: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0 }
]
}
}
];
$self->{maps_counters}->{global_db} = [
{ label => 'db-usage', nlabel => 'database.space.usage.bytes', set => {
key_values => [ { name => 'used' } ],
output_template => 'Used: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0, label_extra_instance => 1 }
]
}
},
{ label => 'db-free', nlabel => 'database.space.free.bytes', set => {
key_values => [ { name => 'free' } ],
output_template => 'Free: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0, label_extra_instance => 1 }
]
}
}
];
$self->{maps_counters}->{table} = [
{ label => 'table-usage', nlabel => 'table.space.usage.bytes', set => {
key_values => [ { name => 'used' }, { name => 'display' } ],
output_template => 'Used: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0, label_extra_instance => 1 }
]
}
},
{ label => 'table-free', nlabel => 'table.space.free.bytes', set => {
key_values => [ { name => 'free' }, { name => 'display' } ],
output_template => 'Free: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%s', unit => 'B', min => 0, label_extra_instance => 1 }
]
}
},
{ label => 'table-frag', nlabel => 'table.fragmentation.percentage', set => {
key_values => [ { name => 'frag' }, { name => 'display' } ],
output_template => 'Fragmentation: %.2f %%',
perfdatas => [
{ template => '%.2f', unit => '%', min => 0, max => 100, label_extra_instance => 1 }
]
}
}
];
}
sub skip_global {
my ($self, %options) = @_;
scalar(keys %{$self->{database}}) > 1 ? return(0) : return(1);
}
sub prefix_global_output {
my ($self, %options) = @_;
return "Databases Total ";
}
sub prefix_database_output {
my ($self, %options) = @_;
return "Database '" . $options{instance_value}->{display} . "' ";
}
sub database_long_output {
my ($self, %options) = @_;
return "Checking Database '" . $options{instance_value}->{display} . "'";
}
sub prefix_table_output {
my ($self, %options) = @_;
return "Table '" . $options{instance_value}->{display} . "' ";
}
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-database:s' => { name => 'filter_database' },
'filter-table:s' => { name => 'filter_table' }
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
if (!($options{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported.");
$self->{output}->option_exit();
}
$options{sql}->query(
query => q{show variables like 'innodb_file_per_table'}
);
my ($name, $value) = $options{sql}->fetchrow_array();
my $innodb_per_table = 0;
$innodb_per_table = 1 if ($value =~ /on/i);
$options{sql}->query(
query => q{
SELECT
table_schema,
table_name,
engine,
data_free,
data_length + index_length as data_used,
DATA_FREE / (DATA_LENGTH + INDEX_LENGTH + DATA_FREE) as TAUX_FRAG
FROM
information_schema.tables
WHERE
table_type = 'BASE TABLE'
AND engine IN ('InnoDB', 'MyISAM')
}
);
my $result = $options{sql}->fetchall_arrayref();
my $innodb_ibdata_done = 0;
$self->{global} = { free => 0, used => 0 };
$self->{database} = {};
foreach my $row (@$result) {
if (defined($self->{option_results}->{filter_database}) && $self->{option_results}->{filter_database} ne '' &&
$row->[0] !~ /$self->{option_results}->{filter_database}/) {
$self->{output}->output_add(long_msg => "skipping '" . $row->[0] . '.' . $row->[1] . "': no matching filter.", debug => 1);
next
}
if (defined($self->{option_results}->{filter_table}) && $self->{option_results}->{filter_table} ne '' &&
$row->[1] !~ /$self->{option_results}->{filter_table}/) {
$self->{output}->output_add(long_msg => "skipping '" . $row->[0] . '.' . $row->[1] . "': no matching filter.", debug => 1);
next
}
if (!defined($self->{database}->{$row->[0]})) {
$self->{database}->{$row->[0]} = {
display => $row->[0],
global_db => { free => 0, used => 0 },
table => {}
};
}
$self->{database}->{$row->[0]}->{table}->{$row->[1]} = {
display => $row->[1]
};
# For a table located in the shared tablespace, this is the free space of the shared tablespace.
if ($row->[2] !~ /innodb/i || $innodb_per_table == 1) {
$self->{global}->{free} += $row->[3];
$self->{database}->{$row->[0]}->{global_db}->{free} += $row->[3];
$self->{database}->{$row->[0]}->{table}->{$row->[1]}->{free} = $row->[3];
$self->{database}->{$row->[0]}->{table}->{$row->[1]}->{frag} = $row->[5];
} elsif ($innodb_ibdata_done == 0) {
$self->{global}->{free} += $row->[3];
$innodb_ibdata_done = 1;
}
$self->{global}->{used} += $row->[4];
$self->{database}->{$row->[0]}->{global_db}->{used} += $row->[4];
$self->{database}->{$row->[0]}->{table}->{$row->[1]}->{used} = $row->[4];
}
if (scalar(keys %{$self->{database}}) <= 0) {
$self->{output}->add_option_msg(short_msg => "No database found.");
$self->{output}->option_exit();
}
}
1;
=head1 MODE
Check MySQL/MariaDB databases and tables sizes.
=over 8
=item B<--filter-database>
Filter the databases to monitor with a regular expression.
=item B<--filter-table>
Filter tables by name (can be a regexp).
=item B<--warning-*> B<--critical-*>
Thresholds (can be: 'total-usage', 'total-free', 'db-usage',
'db-free', 'table-usage', 'table-free', 'table-frag').
=back
=cut
DATABASE_MYSQL_MODE_DATABASESSIZE
$fatpacked{"database/mysql/mode/innodbbufferpoolhitrate.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_INNODBBUFFERPOOLHITRATE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::innodbbufferpoolhitrate;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::statefile;
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 => {
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' },
'lookback' => { name => 'lookback' }
});
$self->{statefile_cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
$self->{statefile_cache}->check_options(%options);
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{sql}->connect();
if (!($self->{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW /*!50000 global */ STATUS WHERE Variable_name IN ('Innodb_buffer_pool_read_requests', 'Innodb_buffer_pool_reads')});
my $new_datas = {Innodb_buffer_pool_read_requests => undef, Innodb_buffer_pool_reads => undef};
my $result = $self->{sql}->fetchall_arrayref();
foreach my $row (@{$result}) {
$new_datas->{$$row[0]} = $$row[1];
}
foreach (keys %$new_datas) {
if (!defined($new_datas->{$_})) {
$self->{output}->add_option_msg(short_msg => "Cannot get '$_' variable.");
$self->{output}->option_exit();
}
}
$self->{statefile_cache}->read(statefile => 'mysql_' . $self->{mode} . '_' . $self->{sql}->get_unique_id4save());
my $old_timestamp = $self->{statefile_cache}->get(name => 'last_timestamp');
$new_datas->{last_timestamp} = time();
my $old_read_request = $self->{statefile_cache}->get(name => 'Innodb_buffer_pool_read_requests');
my $old_read = $self->{statefile_cache}->get(name => 'Innodb_buffer_pool_reads');
if (defined($old_read_request) && defined($old_read) &&
$new_datas->{Innodb_buffer_pool_read_requests} >= $old_read_request &&
$new_datas->{Innodb_buffer_pool_reads} >= $old_read) {
my %prcts = ();
my $total_read_requests = $new_datas->{Innodb_buffer_pool_read_requests} - $old_read_request;
my $total_read_disk = $new_datas->{Innodb_buffer_pool_reads} - $old_read;
$prcts{bufferpool_hitrate_now} = ($total_read_requests == 0) ? 100 : ($total_read_requests - $total_read_disk) * 100 / $total_read_requests;
$prcts{bufferpool_hitrate} = ($new_datas->{Innodb_buffer_pool_read_requests} == 0) ? 100 : ($new_datas->{Innodb_buffer_pool_read_requests} - $new_datas->{Innodb_buffer_pool_reads}) * 100 / $new_datas->{Innodb_buffer_pool_read_requests};
my $exit_code = $self->{perfdata}->threshold_check(value => $prcts{'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now' )}, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf("innodb buffer pool hitrate at %.2f%%", $prcts{'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')})
);
$self->{output}->perfdata_add(
label => 'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now'),
nlabel => 'database.bufferpool.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.average' : '.delta') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')}),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
$self->{output}->perfdata_add(
label => 'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : ''),
nlabel => 'database.bufferpool.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.delta' : '.average') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'bufferpool_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : '')}),
min => 0
);
}
$self->{statefile_cache}->write(data => $new_datas);
if (!defined($old_timestamp)) {
$self->{output}->output_add(
severity => 'OK',
short_msg => "Buffer creation..."
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check hitrate in the InnoDB Buffer Pool.
=over 8
=item B<--warning>
Warning threshold.
=item B<--critical>
Critical threshold.
=item B<--lookback>
Threshold isn't on the percent calculated from the difference ('bufferpool_hitrate_now').
=back
=cut
DATABASE_MYSQL_MODE_INNODBBUFFERPOOLHITRATE
$fatpacked{"database/mysql/mode/longqueries.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_LONGQUERIES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::longqueries;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
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 => {
'warning:s' => { name => 'warning', },
'critical:s' => { name => 'critical', },
'seconds:s' => { name => 'seconds', default => 60 },
'filter-user:s' => { name => 'filter_user' },
'filter-command:s' => { name => 'filter_command', default => '^(?!(sleep)$)' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
if (!defined($self->{option_results}->{seconds}) || $self->{option_results}->{seconds} !~ /^[0-9]+$/) {
$self->{output}->add_option_msg(short_msg => "Please set the option --seconds.");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
$self->{sql} = $options{sql};
$self->{sql}->connect();
$self->{sql}->query(query => q{SELECT USER, COMMAND, TIME, INFO FROM information_schema.processlist ORDER BY TIME DESC});
my $long_queries = 0;
my @queries = ();
while ((my $row = $self->{sql}->fetchrow_hashref())) {
next if (defined($self->{option_results}->{filter_user}) && $self->{option_results}->{filter_user} ne '' &&
$row->{USER} !~ /$self->{option_results}->{filter_user}/i);
next if (defined($self->{option_results}->{filter_command}) && $self->{option_results}->{filter_command} ne '' &&
$row->{COMMAND} !~ /$self->{option_results}->{filter_command}/i);
if (defined($self->{option_results}->{seconds}) && $self->{option_results}->{seconds} ne '' && $row->{TIME} >= $self->{option_results}->{seconds}) {
push @queries, { time => $row->{TIME}, query => $row->{INFO} };
$long_queries++;
}
}
my $exit_code = $self->{perfdata}->threshold_check(value => $long_queries, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf(
"%s queries over %s seconds",
$long_queries, $self->{option_results}->{seconds}
)
);
$self->{output}->perfdata_add(
label => 'longqueries',
nlabel => 'database.longqueries.count',
value => $long_queries,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
for (my $i = 0; $i < 10 && $i < scalar(@queries); $i++) {
$queries[$i]->{query} =~ s/\|/-/mg if (defined($queries[$i]->{query}));
$self->{output}->output_add(long_msg =>
sprintf(
"[time: %s] [query: %s]",
$queries[$i]->{time},
defined($queries[$i]->{query}) ? substr($queries[$i]->{query}, 0, 1024) : '-'
)
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check current number of long queries.
=over 8
=item B<--warning>
Warning threshold (number of long queries).
=item B<--critical>
Critical threshold (number of long queries).
=item B<--seconds>
The minimum execution time in seconds for a long query (default: 60).
=item B<--filter-user>
Filter by user (can be a regexp).
=item B<--filter-command>
Filter by command (can be a regexp. Default: '^(?!(sleep)$)').
=back
=cut
DATABASE_MYSQL_MODE_LONGQUERIES
$fatpacked{"database/mysql/mode/myisamkeycachehitrate.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_MYISAMKEYCACHEHITRATE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::myisamkeycachehitrate;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::statefile;
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 => {
'warning:s' => { name => 'warning', },
'critical:s' => { name => 'critical', },
'lookback' => { name => 'lookback' }
});
$self->{statefile_cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
$self->{statefile_cache}->check_options(%options);
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{sql}->connect();
if (!($self->{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW /*!50000 global */ STATUS WHERE Variable_name IN ('Key_read_requests', 'Key_reads')});
my $new_datas = {Key_read_requests => undef, Key_reads => undef};
my $result = $self->{sql}->fetchall_arrayref();
foreach my $row (@{$result}) {
$new_datas->{$$row[0]} = $$row[1];
}
foreach (keys %$new_datas) {
if (!defined($new_datas->{$_})) {
$self->{output}->add_option_msg(short_msg => "Cannot get '$_' variable.");
$self->{output}->option_exit();
}
}
$self->{statefile_cache}->read(statefile => 'mysql_' . $self->{mode} . '_' . $self->{sql}->get_unique_id4save());
my $old_timestamp = $self->{statefile_cache}->get(name => 'last_timestamp');
$new_datas->{last_timestamp} = time();
my $old_read_request = $self->{statefile_cache}->get(name => 'Key_read_requests');
my $old_read = $self->{statefile_cache}->get(name => 'Key_reads');
if (defined($old_read_request) && defined($old_read) &&
$new_datas->{Key_read_requests} >= $old_read_request &&
$new_datas->{Key_reads} >= $old_read) {
my %prcts = ();
my $total_read_requests = $new_datas->{Key_read_requests} - $old_read_request;
my $total_read_disk = $new_datas->{Key_reads} - $old_read;
$prcts{keycache_hitrate_now} = ($total_read_requests == 0) ? 100 : ($total_read_requests - $total_read_disk) * 100 / $total_read_requests;
$prcts{keycache_hitrate} = ($new_datas->{Key_read_requests} == 0) ? 100 : ($new_datas->{Key_read_requests} - $new_datas->{Key_reads}) * 100 / $new_datas->{Key_read_requests};
my $exit_code = $self->{perfdata}->threshold_check(value => $prcts{'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now' )}, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf("myisam keycache hitrate at %.2f%%", $prcts{'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')})
);
$self->{output}->perfdata_add(
label => 'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now'),
nlabel => 'database.keycache.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.average' : '.delta') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')}),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
$self->{output}->perfdata_add(
label => 'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : ''),
nlabel => 'database.keycache.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.delta' : '.average') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'keycache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : '')}),
min => 0
);
}
$self->{statefile_cache}->write(data => $new_datas);
if (!defined($old_timestamp)) {
$self->{output}->output_add(
severity => 'OK',
short_msg => "Buffer creation..."
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check hitrate in the Myisam Key Cache.
=over 8
=item B<--warning>
Warning threshold.
=item B<--critical>
Critical threshold.
=item B<--lookback>
Threshold isn't on the percent calculated from the difference ('keycache_hitrate_now').
=back
=cut
DATABASE_MYSQL_MODE_MYISAMKEYCACHEHITRATE
$fatpacked{"database/mysql/mode/openfiles.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_OPENFILES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::openfiles;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
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 => {
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
$options{sql}->connect();
if (!($options{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $options{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$options{sql}->query(query => q{SHOW VARIABLES LIKE 'open_files_limit'});
my ($dummy, $open_files_limit) = $options{sql}->fetchrow_array();
if (!defined($open_files_limit)) {
$self->{output}->add_option_msg(short_msg => "Cannot get open files limit.");
$self->{output}->option_exit();
}
$options{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Open_files'});
($dummy, my $open_files) = $options{sql}->fetchrow_array();
if (!defined($open_files)) {
$self->{output}->add_option_msg(short_msg => "Cannot get open files.");
$self->{output}->option_exit();
}
my $prct_open = 100 * $open_files / $open_files_limit;
my $exit_code = $self->{perfdata}->threshold_check(value => $prct_open, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf(
"%.2f%% of the open files limit reached (%s of max. %s)",
$prct_open, $open_files, $open_files_limit
)
);
$self->{output}->perfdata_add(
label => 'open_files',
nlabel => 'database.open.files.count',
value => $open_files,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning', total => $open_files_limit, cast_int => 1),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical', total => $open_files_limit, cast_int => 1),
min => 0
);
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check number of open files.
=over 8
=item B<--warning>
Warning threshold in percent.
=item B<--critical>
Critical threshold in percent.
=back
=cut
DATABASE_MYSQL_MODE_OPENFILES
$fatpacked{"database/mysql/mode/opentables.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_OPENTABLES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::opentables;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
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 => {
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{sql}->connect();
if (!($self->{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW VARIABLES LIKE 'table_open_cache'});
my ($dummy, $open_tables_limit) = $self->{sql}->fetchrow_array();
if (!defined($open_tables_limit)) {
$self->{output}->add_option_msg(short_msg => "Cannot get open table limit.");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Open_tables'});
($dummy, my $open_tables) = $self->{sql}->fetchrow_array();
if (!defined($open_tables)) {
$self->{output}->add_option_msg(short_msg => "Cannot get open tables.");
$self->{output}->option_exit();
}
my $prct_open = int(100 * $open_tables / $open_tables_limit);
my $exit_code = $self->{perfdata}->threshold_check(value => $prct_open, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf(
"%.2f%% of the open files limit reached (%d of max. %d)",
$prct_open, $open_tables, $open_tables_limit
)
);
$self->{output}->perfdata_add(
label => 'open_tables',
nlabel => 'database.open.tables.count',
value => $open_tables,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning', total => $open_tables_limit, cast_int => 1),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical', total => $open_tables_limit, cast_int => 1),
min => 0
);
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check number of open tables.
=over 8
=item B<--warning>
Warning threshold in percent.
=item B<--critical>
Critical threshold in percent.
=back
=cut
DATABASE_MYSQL_MODE_OPENTABLES
$fatpacked{"database/mysql/mode/passwordexpiration.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_PASSWORDEXPIRATION';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::passwordexpiration;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use DateTime;
use centreon::plugins::misc;
use centreon::plugins::statefile;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_status_output {
my ($self, %options) = @_;
return sprintf(
"[user: %s] [password updated: %s] [expired: %s] expire in: %s",
$self->{result_values}->{user},
scalar(localtime($self->{result_values}->{password_last_changed})),
$self->{result_values}->{expire} eq 'never' ? $self->{result_values}->{expire} : $self->{result_values}->{expire} . ' days',
centreon::plugins::misc::change_seconds(value => $self->{result_values}->{expire_time})
);
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'users', type => 2, format_output => '%s user(s) detected', display_counter_problem => { label => 'users', min => 0 },
group => [ { name => 'user', skipped_code => { -11 => 1 } } ]
}
];
$self->{maps_counters}->{user} = [
{ label => 'status', type => 2, critical_default => '%{expire} ne "never" and %{expire_time} == 0', set => {
key_values => [
{ name => 'user' }, { name => 'expire' },
{ name => 'expire_time' }, { name => 'password_last_changed' }
],
closure_custom_output => $self->can('custom_status_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
});
return $self;
}
sub get_database_timezone {
my ($self, %options) = @_;
$options{sql}->query(
query => q{SELECT @@GLOBAL.time_zone, @@system_time_zone}
);
my @row = $options{sql}->fetchrow_array();
my $timezone = $row[0];
if ($row[0] eq 'SYSTEM') {
$timezone = $row[1];
}
return $timezone;
}
sub get_expire_time {
my ($self, %options) = @_;
my $current_time = time();
my $dt = DateTime->from_epoch(epoch => $options{epoch}, time_zone => $options{timezone});
$dt->add(days => $options{days});
my $expire_in = $dt->epoch() - time();
$expire_in = 0 if ($expire_in < 0);
return $expire_in;
}
sub get_password_mariadb {
my ($self, %options) = @_;
my $timezone = $self->get_database_timezone(sql => $options{sql});
$options{sql}->query(
query => q{show variables like 'default_password_lifetime'}
);
my ($name, $default_password_lifetime) = $options{sql}->fetchrow_array();
my $query = q{
SELECT Host, User,
JSON_EXTRACT(Priv, '$.password_last_changed') as password_last_changed,
JSON_EXTRACT(Priv, '$.password_lifetime') as password_lifetime
FROM mysql.global_priv
};
$options{sql}->query(query => $query);
my $i = 1;
while ((my @row = $options{sql}->fetchrow_array())) {
my $expire = 'never';
if ((!defined($row[3]) || $row[3] == -1) && $default_password_lifetime > 0) {
$expire = $default_password_lifetime;
} elsif (defined($row[3]) && $row[3] > 0) {
$expire = $row[3];
}
my $expire_time = 0;
if ($expire ne 'never') {
$expire_time = $self->get_expire_time(
epoch => $row[2],
days => $expire,
timezone => $timezone
);
}
$self->{users}->{global}->{user}->{$i} = {
user => $row[0] . '@' . $row[1],
password_last_changed => $row[2],
expire => $expire,
expire_time => $expire_time
};
$i++;
}
}
sub get_password_mysql {
my ($self, %options) = @_;
my $timezone = $self->get_database_timezone(sql => $options{sql});
$options{sql}->query(
query => q{show variables like 'default_password_lifetime'}
);
my ($name, $default_password_lifetime) = $options{sql}->fetchrow_array();
my $query = q{
SELECT User, Host, UNIX_TIMESTAMP(password_last_changed), password_lifetime
FROM mysql.user
};
$options{sql}->query(query => $query);
my $i = 1;
while ((my @row = $options{sql}->fetchrow_array())) {
my $expire = 'never';
if (!defined($row[3]) && $default_password_lifetime > 0) {
$expire = $default_password_lifetime;
} elsif (defined($row[3]) && $row[3] > 0) {
$expire = $row[3];
}
my $expire_time = 0;
if ($expire ne 'never') {
$expire_time = $self->get_expire_time(
epoch => $row[2],
days => $expire,
timezone => $timezone
);
}
$self->{users}->{global}->{user}->{$i} = {
user => $row[0] . '@' . $row[1],
password_last_changed => $row[2],
expire => $expire,
expire_time => $expire_time
};
$i++;
}
}
sub manage_selection {
my ($self, %options) = @_;
$self->{users}->{global} = { user => {} };
$options{sql}->connect();
if ($options{sql}->is_mariadb() && $options{sql}->is_version_minimum(version => '10.4.3')) {
$self->get_password_mariadb(sql => $options{sql});
} elsif (!$options{sql}->is_mariadb() && $options{sql}->is_version_minimum(version => '5.7.4')) {
$self->get_password_mysql(sql => $options{sql});
} else {
$self->{output}->add_option_msg(short_msg => 'unsupported password policy.');
$self->{output}->option_exit();
}
}
1;
=head1 MODE
Check user password expiration.
=over 8
=item B<--warning-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{user}, %{expire}, %{expire_time}
=item B<--critical-status>
Define the conditions to match for the status to be CRITICAL (default: '%{expire} ne "never" and %{expire_time} == 0').
You can use the following variables: %{user}, %{expire}, %{expire_time}
=back
=cut
DATABASE_MYSQL_MODE_PASSWORDEXPIRATION
$fatpacked{"database/mysql/mode/qcachehitrate.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_QCACHEHITRATE';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::qcachehitrate;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::statefile;
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 => {
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' },
'lookback' => { name => 'lookback' }
});
$self->{statefile_cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
$self->{statefile_cache}->check_options(%options);
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{sql}->connect();
if (!($self->{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW /*!50000 global */ STATUS WHERE Variable_name IN ('Com_select', 'Qcache_hits')});
my $new_datas = {Com_select => undef, Qcache_hits => undef};
my $result = $self->{sql}->fetchall_arrayref();
foreach my $row (@{$result}) {
$new_datas->{$$row[0]} = $$row[1];
}
foreach (keys %$new_datas) {
if (!defined($new_datas->{$_})) {
$self->{output}->add_option_msg(short_msg => "Cannot get '$_' variable.");
$self->{output}->option_exit();
}
}
$self->{sql}->query(query => q{SHOW VARIABLES WHERE Variable_name IN ('have_query_cache', 'query_cache_size')});
my ($dummy, $have_query_cache) = $self->{sql}->fetchrow_array();
if (!defined($have_query_cache)) {
$self->{output}->add_option_msg(short_msg => "Cannot get have_query_cache variable.");
$self->{output}->option_exit();
}
($dummy, my $query_cache_size) = $self->{sql}->fetchrow_array();
if (!defined($query_cache_size)) {
$self->{output}->add_option_msg(short_msg => "Cannot get query_cache_size variable.");
$self->{output}->option_exit();
}
if ($have_query_cache !~ /^yes$/i || $query_cache_size == 0) {
$self->{output}->output_add(
severity => 'OK',
short_msg => "Query cache is turned off."
);
$self->{output}->display();
$self->{output}->exit();
}
$self->{statefile_cache}->read(statefile => 'mysql_' . $self->{mode} . '_' . $self->{sql}->get_unique_id4save());
my $old_timestamp = $self->{statefile_cache}->get(name => 'last_timestamp');
$new_datas->{last_timestamp} = time();
my $old_com_select = $self->{statefile_cache}->get(name => 'Com_select');
my $old_qcache_hits = $self->{statefile_cache}->get(name => 'Qcache_hits');
if (defined($old_com_select) && defined($old_qcache_hits) &&
$new_datas->{Com_select} >= $old_com_select &&
$new_datas->{Qcache_hits} >= $old_qcache_hits) {
my %prcts = ();
my $total_select_requests = ($new_datas->{Com_select} - $old_com_select) + ($new_datas->{Qcache_hits} - $old_qcache_hits);
my $total_hits = $new_datas->{Qcache_hits} - $old_qcache_hits;
$prcts{qcache_hitrate_now} = ($total_select_requests == 0) ? 100 : ($total_hits) * 100 / $total_select_requests;
$prcts{qcache_hitrate} = (($new_datas->{Qcache_hits} + $new_datas->{Com_select}) == 0) ? 100 : ($new_datas->{Qcache_hits}) * 100 / ($new_datas->{Qcache_hits} + $new_datas->{Com_select});
my $exit_code = $self->{perfdata}->threshold_check(value => $prcts{'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now' )}, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf("query cache hitrate at %.2f%%", $prcts{'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')})
);
$self->{output}->perfdata_add(
label => 'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now'),
nlabel => 'database.qcache.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.average' : '.delta') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '' : '_now')}),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
$self->{output}->perfdata_add(
label => 'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : ''),
nlabel => 'database.qcache.hitrate' . ((defined($self->{option_results}->{lookback})) ? '.delta' : '.average') . '.percentage',
unit => '%',
value => sprintf("%.2f", $prcts{'qcache_hitrate' . ((defined($self->{option_results}->{lookback})) ? '_now' : '')}),
min => 0
);
}
$self->{statefile_cache}->write(data => $new_datas);
if (!defined($old_timestamp)) {
$self->{output}->output_add(
severity => 'OK',
short_msg => "Buffer creation..."
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check hitrate in the Query Cache.
=over 8
=item B<--warning>
Warning threshold.
=item B<--critical>
Critical threshold.
=item B<--lookback>
Threshold isn't on the percent calculated from the difference ('qcache_hitrate_now').
=back
=cut
DATABASE_MYSQL_MODE_QCACHEHITRATE
$fatpacked{"database/mysql/mode/queries.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_QUERIES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::queries;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use Digest::MD5 qw(md5_hex);
sub prefix_output {
my ($self, %options) = @_;
return "Requests ";
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, cb_prefix_output => 'prefix_output' }
];
$self->{maps_counters}->{global} = [
{ label => 'total', nlabel => 'queries.total.persecond', set => {
key_values => [ { name => 'Queries', per_second => 1 } ],
output_template => 'Total : %d',
perfdatas => [
{ label => 'total_requests', template => '%d', unit => '/s', min => 0 }
]
}
}
];
foreach ('update', 'delete', 'insert', 'truncate', 'select', 'commit', 'begin') {
push @{$self->{maps_counters}->{global}}, {
label => $_, nlabel => 'queries.' . $_ . '.persecond', display_ok => 0, set => {
key_values => [ { name => 'Com_' . $_, per_second => 1 } ],
output_template => $_ . ' : %d',
perfdatas => [
{ label => $_ . '_requests', template => '%d', unit => '/s', min => 0 }
]
}
};
push @{$self->{maps_counters}->{global}}, {
label => $_ . '-count', , nlabel => 'queries.' . $_ . '.count', display_ok => 0, set => {
key_values => [ { name => 'Com_' . $_, diff => 1 } ],
output_template => $_ . ' count : %d',
perfdatas => [
{ label => $_ . '_count', template => '%d', min => 0 }
]
}
};
}
}
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 => {
});
return $self;
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
if (!($options{sql}->is_version_minimum(version => '5.0.76'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.0.76').");
$self->{output}->option_exit();
}
$options{sql}->query(query => q{
SHOW /*!50000 global */ STATUS WHERE Variable_name IN ('Queries', 'Com_update', 'Com_delete', 'Com_insert', 'Com_truncate', 'Com_select', 'Com_commit', 'Com_begin')
});
$self->{global} = {};
my $result = $options{sql}->fetchall_arrayref();
foreach my $row (@{$result}) {
$self->{global}->{$$row[0]} = $$row[1];
}
$self->{cache_name} = "mysql_" . $self->{mode} . '_' . $options{sql}->get_unique_id4save() . '_' .
(defined($self->{option_results}->{filter_counters}) ? md5_hex($self->{option_results}->{filter_counters}) : md5_hex('all'));
}
1;
=head1 MODE
Check average number of queries executed.
=over 8
=item B<--warning-*>
Warning threshold.
Can be: 'total', 'update', 'insert', 'delete', 'truncate',
'select', 'begin', 'commit'.
=item B<--critical-*>
Critical threshold.
Can be: 'total', 'update', 'insert', 'delete', 'truncate',
'select', 'begin', 'commit'.
=back
=cut
DATABASE_MYSQL_MODE_QUERIES
$fatpacked{"database/mysql/mode/replication.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_REPLICATION';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::replication;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng);
sub custom_connection_output {
my ($self, %options) = @_;
return sprintf(
'connection status: %s%s',
$self->{result_values}->{status},
$self->{result_values}->{status} ne 'ok' ? ' [error message: ' . $self->{result_values}->{error_message} . ']' : ''
);
}
sub custom_thread_sql_output {
my ($self, %options) = @_;
return sprintf(
'thread sql running: %s%s',
$self->{result_values}->{running},
$self->{result_values}->{running} ne 'yes' ? ' [last error message: ' . $self->{result_values}->{error_message} . ']' : ''
);
}
sub custom_thread_io_output {
my ($self, %options) = @_;
return sprintf(
'thread io running: %s%s',
$self->{result_values}->{running},
$self->{result_values}->{running} ne 'yes' ? ' [last error message: ' . $self->{result_values}->{error_message} . ']' : ''
);
}
sub custom_replication_output {
my ($self, %options) = @_;
return sprintf(
'replication status: %s',
$self->{result_values}->{replication_status}
);
}
sub server_long_output {
my ($self, %options) = @_;
return "checking database instance '" . $options{instance_value}->{display} . "'";
}
sub prefix_server_output {
my ($self, %options) = @_;
return "database instance '" . $options{instance_value}->{display} . "' ";
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0 },
{ name => 'servers', type => 3, cb_prefix_output => 'prefix_server_output', cb_long_output => 'server_long_output', indent_long_output => ' ', message_multiple => 'All database instances are ok',
group => [
{ name => 'connection', type => 0, skipped_code => { -10 => 1 } },
{ name => 'thread_sql', type => 0, skipped_code => { -10 => 1 } },
{ name => 'thread_io', type => 0, skipped_code => { -10 => 1 } },
{ name => 'position', type => 0, skipped_code => { -10 => 1 } }
]
}
];
$self->{maps_counters}->{global} = [
{ label => 'slaves-running', nlabel => 'instance.slaves.running.count', set => {
key_values => [ { name => 'slaves_running' }, { name => 'total' } ],
output_template => 'number of slave instances running: %s',
perfdatas => [
{ template => '%s', min => 0, max => 'total' }
]
}
}
];
$self->{maps_counters}->{connection} = [
{
label => 'connection-status',
type => 2,
critical_default => '%{status} ne "ok"',
set => {
key_values => [ { name => 'status' }, { name => 'error_message' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_connection_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
$self->{maps_counters}->{thread_sql} = [
{
label => 'thread-sql-status',
type => 2,
set => {
key_values => [ { name => 'running' }, { name => 'error_message' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_thread_sql_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
$self->{maps_counters}->{thread_io} = [
{
label => 'thread-io-status',
type => 2,
set => {
key_values => [ { name => 'running' }, { name => 'error_message' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_thread_io_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
$self->{maps_counters}->{position} = [
{ label => 'slave-latency', nlabel => 'instance.slave.latency.seconds', set => {
key_values => [ { name => 'latency' } ],
output_template => 'slave has %s seconds latency behind master',
perfdatas => [
{ template => '%d', unit => 's', label_extra_instance => 1 }
]
}
},
{
label => 'replication-status',
unknown_default => '%{replication_status} =~ /configurationIssue/i',
warning_default => '%{replication_status} =~ /inProgress/i',
critical_default => '%{replication_status} =~ /connectIssueToMaster/i',
type => 2,
set => {
key_values => [ { name => 'replication_status' }, { name => 'display' } ],
closure_custom_output => $self->can('custom_replication_output'),
closure_custom_perfdata => sub { return 0; },
closure_custom_threshold_check => \&catalog_status_threshold_ng
}
}
];
}
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
$options{options}->add_options(arguments => {
});
return $self;
}
sub sql_query_show_slave_status {
my ($self, %options) = @_;
if ($options{sql}->is_mariadb() && $options{sql}->is_version_minimum(version => '10.2.x')) {
$options{sql}->query(query => q{
SHOW ALL SLAVES STATUS
});
} else {
$options{sql}->query(query => q{
SHOW SLAVE STATUS
});
}
}
sub check_connection {
my ($self, %options) = @_;
my ($exit, $msg_error) = $options{sql}->connect(dontquit => 1);
if ($exit == -1) {
$self->{servers}->{ $options{name} }->{connection}->{status} = 'error';
$self->{servers}->{ $options{name} }->{connection}->{error_message} = $msg_error;
}
}
sub check_slave {
my ($self, %options) = @_;
return if ($self->{servers}->{ $options{name} }->{connection}->{status} ne 'ok');
$self->sql_query_show_slave_status(sql => $options{sql});
my $result = $options{sql}->fetchrow_hashref();
my $slave_running = 0;
if (defined($result->{Slave_IO_Running})) {
my $running = 'no';
if ($result->{Slave_IO_Running} =~ /^yes$/i) {
$slave_running = 1;
$running = 'yes';
}
$self->{servers}->{ $options{name} }->{thread_io} = {
display => $options{name},
running => $running,
error_message => defined($result->{Last_IO_Error}) ? $result->{Last_IO_Error} : ''
};
}
if (defined($result->{Slave_SQL_Running})) {
my $running = 'no';
if ($result->{Slave_SQL_Running} =~ /^yes$/i) {
$slave_running = 1;
$running = 'yes';
}
$self->{servers}->{ $options{name} }->{thread_sql} = {
display => $options{name},
running => $running,
error_message => defined($result->{Last_Error}) ? $result->{Last_Error} : ''
};
}
$self->{servers}->{ $options{name} }->{is_slave} = $slave_running;
$self->{global}->{slaves_running} += $slave_running;
}
sub check_master_slave_position {
my ($self, %options) = @_;
return if ($self->{servers}->{ $options{name_master} }->{connection}->{status} ne 'ok');
return if ($self->{servers}->{ $options{name_slave} }->{connection}->{status} ne 'ok');
return if ($self->{servers}->{ $options{name_slave} }->{is_slave} == 0);
$options{sql_master}->query(query => q{
SHOW MASTER STATUS
});
my $master_result = $options{sql_master}->fetchrow_hashref();
$self->sql_query_show_slave_status(sql => $options{sql_slave});
my $slave_result = $options{sql_slave}->fetchrow_hashref();
$self->{servers}->{ $options{name_slave} }->{position} = {
display => $options{name_slave},
latency => $slave_result->{Seconds_Behind_Master},
replication_status => 'ok'
};
$options{sql_slave}->query(query => q{
SHOW FULL PROCESSLIST
});
my ($slave_sql_thread_ko, $slave_sql_thread_warning, $slave_sql_thread_ok) = (1, 1, 1);
while ((my $row = $options{sql_slave}->fetchrow_hashref())) {
my $state = $row->{State};
$slave_sql_thread_ko = 0 if (defined($state) && $state =~ /^(Waiting to reconnect after a failed binlog dump request|Connecting to master|Reconnecting after a failed binlog dump request|Waiting to reconnect after a failed master event read|Waiting for the slave SQL thread to free enough relay log space)$/i);
$slave_sql_thread_warning = 0 if (defined($state) && $state =~ /^Waiting for the next event in relay log|Reading event from the relay log$/i);
$slave_sql_thread_ok = 0 if (defined($state) && $state =~ /^Has read all relay log; waiting for the slave I\/O thread to update it$/i);
}
if ($slave_sql_thread_ko == 0) {
$self->{servers}->{ $options{name_slave} }->{position}->{replication_status} = 'connectIssueToMaster';
} elsif (($master_result->{File} ne $slave_result->{Master_Log_File} ||
$master_result->{Position} != $slave_result->{Read_Master_Log_Pos}) &&
($slave_sql_thread_warning == 0 || $slave_sql_thread_ok == 0)
) {
$self->{servers}->{ $options{name_slave} }->{position}->{replication_status} = 'inProgress';
} else {
$master_result->{File} =~ /(\d+)$/;
my $master_bin_num = $1;
$slave_result->{Master_Log_File} =~ /(\d+)$/;
my $slave_bin_num = $1;
my $diff_binlog = abs($master_bin_num - $slave_bin_num);
# surely of missconfiguration of the plugin
if ($diff_binlog > 1 && $slave_result->{Seconds_Behind_Master} < 10) {
$self->{servers}->{ $options{name_slave} }->{position}->{replication_status} = 'configurationIssue';
}
}
}
sub manage_selection {
my ($self, %options) = @_;
if (ref($options{sql}) ne 'ARRAY') {
$self->{output}->add_option_msg(short_msg => "Need to use --multiple options.");
$self->{output}->option_exit();
}
if (scalar(@{$options{sql}}) < 2) {
$self->{output}->add_option_msg(short_msg => "Need to specify two MySQL Server.");
$self->{output}->option_exit();
}
my ($sql_server1, $sql_server2) = @{$options{sql}};
my ($server1_name, $server2_name) = ($sql_server1->get_id(), $sql_server2->get_id());
$self->{global} = {
total => 2,
slaves_running => 0
};
$self->{servers} = {
$server1_name => {
display => $server1_name,
connection => {
display => $server1_name,
status => 'ok',
error_message => ''
}
},
$server2_name => {
display => $server2_name,
connection => {
display => $server2_name,
status => 'ok',
error_message => ''
}
}
};
$self->check_connection(name => $server1_name, sql => $sql_server1);
$self->check_connection(name => $server2_name, sql => $sql_server2);
$self->check_slave(name => $server1_name, sql => $sql_server1);
$self->check_slave(name => $server2_name, sql => $sql_server2);
$self->check_master_slave_position(
name_master => $server1_name,
name_slave => $server2_name,
sql_master => $sql_server1,
sql_slave => $sql_server2
);
$self->check_master_slave_position(
name_master => $server2_name,
name_slave => $server1_name,
sql_master => $sql_server2,
sql_slave => $sql_server1
);
}
1;
=head1 MODE
Check MySQL replication (need to use --multiple).
=over 8
=item B<--unknown-connection-status>
Define the conditions to match for the status to be UNKNOWN.
You can use the following variables: %{status}, %{error_message}, %{display}
=item B<--warning-connection-status>
Define the conditions to match for the status to be WARNING.
You can use the following variables: %{status}, %{error_message}, %{display}
=item B<--critical-connection-status>
Define the conditions to match for the status to be CRITICAL (default: '%{status} ne "ok"').
You can use the following variables: %{status}, %{error_message}, %{display}
=item B<--unknown-replication-status>
Define the conditions to match for the status to be UNKNOWN (default: '%{replication_status} =~ /configurationIssue/i').
You can use the following variables: %{replication_status}, %{display}
=item B<--warning-replication-status>
Define the conditions to match for the status to be WARNING (default: '%{replication_status} =~ /inProgress/i').
You can use the following variables: %{replication_status}, %{display}
=item B<--critical-replication-status>
Define the conditions to match for the status to be CRITICAL (default: '%{replication_status} =~ /connectIssueToMaster/i').
You can use the following variables: %{replication_status}, %{display}
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'slaves-running', 'slave-latency' (s).
=back
=cut
DATABASE_MYSQL_MODE_REPLICATION
$fatpacked{"database/mysql/mode/slowqueries.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_SLOWQUERIES';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::slowqueries;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use centreon::plugins::statefile;
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 => {
'warning:s' => { name => 'warning' },
'critical:s' => { name => 'critical' }
});
$self->{statefile_cache} = centreon::plugins::statefile->new(%options);
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
$self->{statefile_cache}->check_options(%options);
}
sub run {
my ($self, %options) = @_;
# $options{sql} = sqlmode object
$self->{sql} = $options{sql};
$self->{sql}->connect();
if (!($self->{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
$self->{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Slow_queries'});
my ($name, $result) = $self->{sql}->fetchrow_array();
if (!defined($result)) {
$self->{output}->add_option_msg(short_msg => "Cannot get slow queries.");
$self->{output}->option_exit();
}
my $new_datas = {};
$self->{statefile_cache}->read(statefile => 'mysql_' . $self->{mode} . '_' . $self->{sql}->get_unique_id4save());
my $old_timestamp = $self->{statefile_cache}->get(name => 'last_timestamp');
$new_datas->{last_timestamp} = time();
if (defined($old_timestamp) && $new_datas->{last_timestamp} - $old_timestamp == 0) {
$self->{output}->add_option_msg(short_msg => "Need at least one second between two checks.");
$self->{output}->option_exit();
}
$new_datas->{$name} = $result;
my $old_val = $self->{statefile_cache}->get(name => $name);
if (defined($old_val) && $result >= $old_val) {
my $value = ($result - $old_val);
my $exit_code = $self->{perfdata}->threshold_check(value => $value, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
$self->{output}->output_add(
severity => $exit_code,
short_msg => sprintf("%d slow queries since last check.", $value)
);
$self->{output}->perfdata_add(
label => 'slow_queries_delta',
nlabel => 'database.slowqueries.delta.count',
value => $value,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
}
$self->{statefile_cache}->write(data => $new_datas);
if (!defined($old_timestamp)) {
$self->{output}->output_add(
severity => 'OK',
short_msg => "Buffer creation..."
);
}
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check number of slow queries since last check.
=over 8
=item B<--warning>
Warning number for slow queries since last check.
=item B<--critical>
Critical number for slow queries since last check.
=back
=cut
DATABASE_MYSQL_MODE_SLOWQUERIES
$fatpacked{"database/mysql/mode/threadsconnected.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_THREADSCONNECTED';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::threadsconnected;
use base qw(centreon::plugins::templates::counter);
use strict;
use warnings;
sub custom_usage_output {
my ($self, %options) = @_;
return sprintf(
'Client connected threads total: %s used: %s (%.2f%%) free: %s (%.2f%%)',
$self->{result_values}->{total},
$self->{result_values}->{used},
$self->{result_values}->{prct_used},
$self->{result_values}->{free},
$self->{result_values}->{prct_free}
);
}
sub prefix_databse_output {
my ($self, %options) = @_;
return "Database '" . $options{instance_value}->{name} . "' ";
}
sub set_counters {
my ($self, %options) = @_;
$self->{maps_counters_type} = [
{ name => 'global', type => 0, message_separator => ' - ', skipped_code => { -10 => 1 } },
{ name => 'databases', type => 1, cb_prefix_output => 'prefix_database_output', skipped_code => { -10 => 1 } }
];
$self->{maps_counters}->{global} = [
{ label => 'usage', nlabel => 'threads.connected.count', set => {
key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
closure_custom_output => $self->can('custom_usage_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'total' },
]
}
},
{ label => 'usage-prct', nlabel => 'threads.connected.percentage', display_ok => 0, set => {
key_values => [ { name => 'prct_used' }, { name => 'free' }, { name => 'used' }, { name => 'prct_free' }, { name => 'total' } ],
closure_custom_output => $self->can('custom_usage_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
}
];
$self->{maps_counters}->{databases} = [
{ label => 'database-threads-connected', nlabel => 'database.threads.connected.count', display_ok => 0, set => {
key_values => [ { name => 'threads_connected' } ],
output_template => 'threads connected: %s',
perfdatas => [
{ template => '%d', min => 0, label_extra_instance => 1 }
]
}
}
];
}
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 => {
'add-databases' => { name => 'add_databases' }
});
return $self;
}
sub add_databases {
my ($self, %options) = @_;
$options{sql}->query(query => q{
SELECT
schema_name,
SUM(case when DB is not null then 1 else 0 end) as numbers
FROM information_schema.schemata LEFT JOIN information_schema.processlist ON schemata.schema_name = DB
GROUP BY schemata.schema_name
});
$self->{databases} = {};
my $result = $options{sql}->fetchall_arrayref();
foreach my $row (@$result) {
$self->{databases}->{ $row->[0] } = {
name => $row->[0],
threads_connected => $row->[1]
};
}
}
sub manage_selection {
my ($self, %options) = @_;
$options{sql}->connect();
if (!($options{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
my $infos = {};
if (!$options{sql}->is_mariadb() && $options{sql}->is_version_minimum(version => '5.7.6')) {
$options{sql}->query(query => q{
SELECT 'max_connections' as name, @@GLOBAL.max_connections as value
UNION
SELECT VARIABLE_NAME as name, VARIABLE_VALUE as value FROM performance_schema.global_status WHERE VARIABLE_NAME = 'Threads_connected'
});
while (my ($name, $value) = $options{sql}->fetchrow_array()) {
$infos->{lc($name)} = $value;
}
} elsif ($options{sql}->is_version_minimum(version => '5.1.12')) {
$options{sql}->query(query => q{
SELECT 'max_connections' as name, @@GLOBAL.max_connections as value
UNION
SELECT VARIABLE_NAME as name, VARIABLE_VALUE as value FROM information_schema.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Threads_connected'
});
while (my ($name, $value) = $options{sql}->fetchrow_array()) {
$infos->{lc($name)} = $value;
}
} else {
$options{sql}->query(query => q{SELECT 'max_connections' as name, @@GLOBAL.max_connections as value});
if (my ($name, $value) = $options{sql}->fetchrow_array()) {
$infos->{lc($name)} = $value
}
$options{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Threads_connected'});
if (my ($name, $value) = $options{sql}->fetchrow_array()) {
$infos->{lc($name)} = $value
}
}
if (scalar(keys %$infos) == 0) {
$self->{output}->add_option_msg(short_msg => 'Cannot get number of open connections.');
$self->{output}->option_exit();
}
my $prct_used = $infos->{threads_connected} * 100 / $infos->{max_connections};
$self->{global} = {
total => $infos->{max_connections},
used => $infos->{threads_connected},
free => $infos->{max_connections} - $infos->{threads_connected},
prct_used => $prct_used,
prct_free => 100 - $prct_used
};
if (defined($self->{option_results}->{add_databases})) {
$self->add_databases(sql => $options{sql});
}
}
1;
=head1 MODE
Check number of open connections.
=over 8
=item B<--add-databases>
Add threads by databases.
=item B<--warning-*> B<--critical-*>
Thresholds.
Can be: 'usage', 'usage-prct' (%), 'database-threads-connected'.
=back
=cut
DATABASE_MYSQL_MODE_THREADSCONNECTED
$fatpacked{"database/mysql/mode/uptime.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MODE_UPTIME';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mode::uptime;
use base qw(centreon::plugins::mode);
use strict;
use warnings;
use POSIX;
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1);
bless $self, $class;
# Adding options specific to this mode
$options{options}->add_options(arguments => {
"warning:s" => { name => 'warning' },
"critical:s" => { name => 'critical' },
"seconds" => { name => 'seconds' }
});
return $self;
}
sub check_options {
my ($self, %options) = @_;
$self->SUPER::init(%options);
# Validating warning threshold
if (($self->{perfdata}->threshold_validate(label => 'warning', value => $self->{option_results}->{warning})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong warning threshold '" . $self->{option_results}->{warning} . "'.");
$self->{output}->option_exit();
}
# Validating critical threshold
if (($self->{perfdata}->threshold_validate(label => 'critical', value => $self->{option_results}->{critical})) == 0) {
$self->{output}->add_option_msg(short_msg => "Wrong critical threshold '" . $self->{option_results}->{critical} . "'.");
$self->{output}->option_exit();
}
}
sub run {
my ($self, %options) = @_;
$options{sql}->connect();
# Checking if MySQL version is supported
if (!($options{sql}->is_version_minimum(version => '5'))) {
$self->{output}->add_option_msg(short_msg => "MySQL version '" . $self->{sql}->{version} . "' is not supported (need version >= '5.x').");
$self->{output}->option_exit();
}
# Querying MySQL for Uptime status
$options{sql}->query(query => q{SHOW /*!50000 global */ STATUS LIKE 'Uptime'});
my ($name, $value) = $options{sql}->fetchrow_array();
# Handling case where uptime value is not available
if (!defined($value)) {
$self->{output}->add_option_msg(short_msg => "Cannot get uptime.");
$self->{output}->option_exit();
}
# Checking the threshold and determining exit code
my $exit_code = $self->{perfdata}->threshold_check(value => $value, threshold => [ { label => 'critical', exit_litteral => 'critical' }, { label => 'warning', exit_litteral => 'warning' } ]);
# Calculating uptime in days or seconds based on user preference
my $uptime_days = floor($value / 86400);
my $msg = sprintf("database is up since %d days", $uptime_days);
if (defined($self->{option_results}->{seconds})) {
$msg = sprintf("database is up since %d seconds", $value);
}
# Adding start time information to the message
$msg .= sprintf(" (Start time = %s)", strftime("%Y/%m/%d %H:%M:%S", localtime(time - $value)));
# Adding output message and performance data
$self->{output}->output_add(
severity => $exit_code,
short_msg => $msg
);
$self->{output}->perfdata_add(
label => 'uptime',
nlabel => 'database.uptime.seconds',
unit => 's',
value => $value,
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical'),
min => 0
);
# Displaying the output and exiting
$self->{output}->display();
$self->{output}->exit();
}
1;
=head1 MODE
Check MySQL uptime.
=over 8
=item B<--warning>
Warning threshold.
=item B<--critical>
Critical threshold.
=item B<--seconds>
Display uptime in seconds.
=back
=cut
DATABASE_MYSQL_MODE_UPTIME
$fatpacked{"database/mysql/mysqlcmd.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_MYSQLCMD';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::mysqlcmd;
use strict;
use warnings;
use centreon::plugins::misc;
use Digest::MD5 qw(md5_hex);
sub new {
my ($class, %options) = @_;
my $self = {};
bless $self, $class;
# $options{options} = options object
# $options{output} = output object
# $options{exit_value} = integer
# $options{noptions} = integer
if (!defined($options{output})) {
print "Class mysqlcmd: Need to specify 'output' argument.\n";
exit 3;
}
if (!defined($options{options})) {
$options{output}->add_option_msg(short_msg => "Class Mysqlcmd: Need to specify 'options' argument.");
$options{output}->option_exit();
}
if (!defined($options{noptions})) {
$options{options}->add_options(arguments => {
'mysql-cmd:s' => { name => 'mysql_cmd', default => '/usr/bin/mysql' },
'host:s@' => { name => 'host' },
'port:s@' => { name => 'port' },
'username:s@' => { name => 'username' },
'password:s@' => { name => 'password' },
'socket:s@' => { name => 'socket' },
'sql-errors-exit:s' => { name => 'sql_errors_exit', default => 'unknown' }
});
}
$options{options}->add_help(package => __PACKAGE__, sections => 'MYSQL COMMAND OPTIONS', once => 1);
$self->{output} = $options{output};
$self->{sqlmode_name} = $options{sqlmode_name};
$self->{args} = undef;
$self->{stdout} = undef;
$self->{columns} = undef;
$self->{version} = undef;
$self->{host} = undef;
$self->{port} = undef;
$self->{socket} = undef;
$self->{username} = undef;
$self->{password} = undef;
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->{sqlmode_name}) {
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->{host} = (defined($self->{option_results}->{host})) ? shift(@{$self->{option_results}->{host}}) : undef;
$self->{port} = (defined($self->{option_results}->{port})) ? shift(@{$self->{option_results}->{port}}) : undef;
$self->{socket} = (defined($self->{option_results}->{socket})) ? shift(@{$self->{option_results}->{socket}}) : undef;
$self->{username} = (defined($self->{option_results}->{username})) ? shift(@{$self->{option_results}->{username}}) : undef;
$self->{password} = (defined($self->{option_results}->{password})) ? shift(@{$self->{option_results}->{password}}) : undef;
$self->{sql_errors_exit} = $self->{option_results}->{sql_errors_exit};
$self->{mysql_cmd} = $self->{option_results}->{mysql_cmd};
if (!defined($self->{host}) || $self->{host} eq '') {
$self->{output}->add_option_msg(short_msg => 'Need to specify host argument.');
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
$self->{args} = ['--batch', '--raw', '--host', $self->{host}];
if (defined($self->{port})) {
push @{$self->{args}}, "--port", $self->{port};
}
if (defined($self->{username})) {
push @{$self->{args}}, "--user", $self->{username};
}
if (defined($self->{password}) && $self->{password} ne '') {
push @{$self->{args}}, "-p" . $self->{password};
}
if (defined($self->{socket}) && $self->{socket} ne '') {
push @{$self->{args}}, "--socket", $self->{socket};
}
if (scalar(@{$self->{option_results}->{host}}) == 0) {
return 0;
}
return 1;
}
sub is_version_minimum {
my ($self, %options) = @_;
# $options{version} = string version to check
my @version_src = split /\./, $self->{version};
my @versions = split /\./, $options{version};
for (my $i = 0; $i < scalar(@versions); $i++) {
return 1 if ($versions[$i] eq 'x');
return 1 if (!defined($version_src[$i]));
$version_src[$i] =~ /^([0-9]*)/;
next if ($versions[$i] == int($1));
return 0 if ($versions[$i] > int($1));
return 1 if ($versions[$i] < int($1));
}
return 1;
}
sub get_id {
my ($self, %options) = @_;
my $msg = $self->{host};
if (defined($self->{port})) {
$msg .= ":" . $self->{port};
}
return $msg;
}
sub get_unique_id4save {
my ($self, %options) = @_;
my $msg = $self->{host};
if (defined($self->{port})) {
$msg .= ":" . $self->{port};
}
return md5_hex($msg);
}
sub quote {
my $self = shift;
return undef;
}
sub command_execution {
my ($self, %options) = @_;
my ($lerror, $stdout, $exit_code) = centreon::plugins::misc::backtick(
command => $self->{mysql_cmd},
arguments => [@{$self->{args}}, '-e', $options{request}],
timeout => 30,
wait_exit => 1,
redirect_stderr => 1
);
if ($exit_code <= -1000) {
if ($exit_code == -1000) {
$self->{output}->output_add(severity => 'UNKNOWN',
short_msg => $stdout);
}
$self->{output}->display();
$self->{output}->exit();
}
return ($exit_code, $stdout);
}
sub disconnect {}
sub is_mariadb {
my ($self) = @_;
return $self->{is_mariadb};
}
sub set_version {
my ($self, %options) = @_;
$self->{is_mariadb} = 0;
$self->{version} = $options{version};
# MariaDB: 5.5.5-10.1.36-MariaDB, 10.1.36-MariaDB or 11.4.4-2-MariaDB-enterprise-log
if ($self->{version} =~ /(?:\d\.\d\.\d-)?(\d+\.\d+\.\d+).*MariaDB/i) {
$self->{version} = $1;
$self->{is_mariadb} = 1;
}
}
sub connect {
my ($self, %options) = @_;
my $dontquit = (defined($options{dontquit}) && $options{dontquit} == 1) ? 1 : 0;
(my $exit_code, $self->{stdout}) = $self->command_execution(request => "SHOW VARIABLES LIKE 'version'");
if ($exit_code != 0) {
if ($dontquit == 0) {
$self->{output}->add_option_msg(short_msg => 'Cannot connect: ' . $self->{stdout});
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
return (-1, 'Cannot connect: ' . $self->{stdout});
}
my $row = $self->fetchrow_hashref();
$self->set_version(version => $row->{Value});
return 0;
}
sub fetchall_arrayref {
my ($self, %options) = @_;
my $array_ref = [];
if (!defined($self->{columns})) {
$self->{stdout} =~ s/^(.*?)(\n|$)//;
@{$self->{columns}} = split(/\t/, $1);
}
foreach (split /\n/, $self->{stdout}) {
push @$array_ref, [map({ s/\\n/\x{0a}/g; s/\\t/\x{09}/g; s/\\/\x{5c}/g; $_; } split(/\t/, $_))];
}
return $array_ref;
}
sub fetchrow_array {
my ($self, %options) = @_;
my @array_result = ();
if (!defined($self->{columns})) {
$self->{stdout} =~ s/^(.*?)(\n|$)//;
@{$self->{columns}} = split(/\t/, $1);
}
if (($self->{stdout} =~ s/^(.*?)(\n|$)//)) {
push @array_result, map({ s/\\n/\x{0a}/g; s/\\t/\x{09}/g; s/\\/\x{5c}/g; $_; } split(/\t/, $1));
}
return @array_result;
}
sub fetchrow_hashref {
my ($self, %options) = @_;
my $array_result = undef;
if (!defined($self->{columns})) {
$self->{stdout} =~ s/^(.*?)(\n|$)//;
@{$self->{columns}} = split(/\t/, $1);
}
if ($self->{stdout} ne '' && $self->{stdout} =~ s/^(.*?)(\n|$)//) {
$array_result = {};
my @values = split(/\t/, $1);
for (my $i = 0; $i < scalar(@values); $i++) {
my $value = $values[$i];
$value =~ s/\\n/\x{0a}/g;
$value =~ s/\\t/\x{09}/g;
$value =~ s/\\/\x{5c}/g;
$array_result->{$self->{columns}[$i]} = $value;
}
}
return $array_result;
}
sub query {
my ($self, %options) = @_;
$self->{columns} = undef;
(my $exit_code, $self->{stdout}) = $self->command_execution(request => $options{query});
if ($exit_code != 0) {
$self->{output}->add_option_msg(short_msg => 'Cannot execute query: ' . $self->{stdout});
$self->{output}->option_exit(exit_litteral => $self->{sql_errors_exit});
}
}
1;
=head1 NAME
MySQL command global
=head1 SYNOPSIS
MySQL command class
=head1 MYSQL COMMAND OPTIONS
=over 8
=item B<--mysql-cmd>
mysql command (default: '/usr/bin/mysql').
=item B<--host>
Database hostname.
=item B<--port>
Database port.
=item B<--username>
Database username.
=item B<--password>
Database password.
=item B<--sql-errors-exit>
Exit code for DB Errors (default: unknown)
=back
=head1 DESCRIPTION
B<snmp>.
=cut
DATABASE_MYSQL_MYSQLCMD
$fatpacked{"database/mysql/plugin.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'DATABASE_MYSQL_PLUGIN';
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
package database::mysql::plugin;
use strict;
use warnings;
use base qw(centreon::plugins::script_sql);
sub new {
my ($class, %options) = @_;
my $self = $class->SUPER::new(package => __PACKAGE__, %options);
bless $self, $class;
$self->{version} = '0.1';
$self->{modes} = {
'backup' => 'database::mysql::mode::backup',
'collection' => 'centreon::common::protocols::sql::mode::collection',
'connection-time' => 'centreon::common::protocols::sql::mode::connectiontime',
'databases-size' => 'database::mysql::mode::databasessize',
'innodb-bufferpool-hitrate' => 'database::mysql::mode::innodbbufferpoolhitrate',
'long-queries' => 'database::mysql::mode::longqueries',
'myisam-keycache-hitrate' => 'database::mysql::mode::myisamkeycachehitrate',
'open-files' => 'database::mysql::mode::openfiles',
'open-tables' => 'database::mysql::mode::opentables',
'password-expiration' => 'database::mysql::mode::passwordexpiration',
'qcache-hitrate' => 'database::mysql::mode::qcachehitrate',
'queries' => 'database::mysql::mode::queries',
'replication' => 'database::mysql::mode::replication',
'slow-queries' => 'database::mysql::mode::slowqueries',
'sql' => 'centreon::common::protocols::sql::mode::sql',
'sql-string' => 'centreon::common::protocols::sql::mode::sqlstring',
'threads-connected' => 'database::mysql::mode::threadsconnected',
'uptime' => 'database::mysql::mode::uptime'
};
$self->{sql_modes}->{dbi} = 'database::mysql::dbi';
$self->{sql_modes}->{mysqlcmd} = 'database::mysql::mysqlcmd';
return $self;
}
sub init {
my ($self, %options) = @_;
$self->{options}->add_options(
arguments => {
'host:s@' => { name => 'db_host' },
'port:s@' => { name => 'db_port' },
'socket:s@' => { name => 'db_socket' }
}
);
$self->{options}->parse_options();
my $options_result = $self->{options}->get_options();
$self->{options}->clean();
if (defined($options_result->{db_host})) {
@{$self->{sqldefault}->{dbi}} = ();
@{$self->{sqldefault}->{mysqlcmd}} = ();
for (my $i = 0; $i < scalar(@{$options_result->{db_host}}); $i++) {
$self->{sqldefault}->{dbi}->[$i] = { data_source => 'mysql:host=' . $options_result->{db_host}[$i] };
$self->{sqldefault}->{mysqlcmd}->[$i] = { host => $options_result->{db_host}[$i] };
if (defined($options_result->{db_port}[$i])) {
$self->{sqldefault}->{dbi}->[$i]->{data_source} .= ';port=' . $options_result->{db_port}[$i];
$self->{sqldefault}->{mysqlcmd}->[$i]->{port} = $options_result->{db_port}[$i];
}
if (defined($options_result->{db_socket}[$i])) {
$self->{sqldefault}->{dbi}->[$i]->{data_source} .= ';mysql_socket=' . $options_result->{db_socket}[$i];
$self->{sqldefault}->{mysqlcmd}->[$i]->{socket} = $options_result->{db_socket}[$i];
}
}
}
$self->SUPER::init(%options);
}
1;
=head1 PLUGIN DESCRIPTION
Check MySQL Server.
=over 8
You can use following options or options from 'sqlmode' directly.
=item B<--host>
Hostname to query.
=item B<--port>
Database Server Port.
=back
=cut
DATABASE_MYSQL_PLUGIN
s/^ //mg for values %fatpacked;
my $class = 'FatPacked::'.(0+\%fatpacked);
no strict 'refs';
*{"${class}::files"} = sub { keys %{$_[0]} };
if ($] < 5.008) {
*{"${class}::INC"} = sub {
if (my $fat = $_[0]{$_[1]}) {
my $pos = 0;
my $last = length $fat;
return (sub {
return 0 if $pos == $last;
my $next = (1 + index $fat, "\n", $pos) || $last;
$_ .= substr $fat, $pos, $next - $pos;
$pos = $next;
return 1;
});
}
};
}
else {
*{"${class}::INC"} = sub {
if (my $fat = $_[0]{$_[1]}) {
open my $fh, '<', \$fat
or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
return $fh;
}
return;
};
}
unshift @INC, bless \%fatpacked, $class;
} # END OF FATPACK CODE
#
# Copyright 2024 Centreon (http://www.centreon.com/)
#
# Centreon is a full-fledged industry-strength solution that meets
# the needs in IT infrastructure and application monitoring for
# service performance.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
use strict;
use warnings;
# Not perl embedded compliant at all
use FindBin;
use lib "$FindBin::Bin";
# use lib '/usr/lib/nagios/plugins/';
use centreon::plugins::script;
centreon::plugins::script->new()->run();