Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
265 changes: 144 additions & 121 deletions src/os/linux/local/mode/memory.pm
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,107 @@
#

package os::linux::local::mode::memory;

use base qw(centreon::plugins::templates::counter);

use strict;
use warnings;

sub custom_memory_perfdata {
my ($self, %options) = @_;
# Perfdata pour bytes utilisés
$self->{output}->perfdata_add(
nlabel => 'memory.usage.bytes',
unit => 'B',
value => $self->{result_values}->{used},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-memory-usage'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-memory-usage'),
min => 0,
max => $self->{result_values}->{total}
);
# Perfdata pour bytes libres
$self->{output}->perfdata_add(
nlabel => 'memory.free.bytes',
unit => 'B',
value => $self->{result_values}->{free},
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-memory-usage-free'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-memory-usage-free'),
min => 0,
max => $self->{result_values}->{total}
);
# Perfdata pour pourcentage utilisé
$self->{output}->perfdata_add(
nlabel => 'memory.usage.percentage',
unit => '%',
value => sprintf("%.2f", $self->{result_values}->{prct_used}),
warning => $self->{perfdata}->get_perfdata_for_output(label => 'warning-memory-usage-prct'),
critical => $self->{perfdata}->get_perfdata_for_output(label => 'critical-memory-usage-prct'),
min => 0,
max => 100
);
# Perfdata pour bytes available
$self->{output}->perfdata_add(
nlabel => 'memory.available.bytes',
unit => 'B',
value => $self->{result_values}->{available},
min => 0,
max => $self->{result_values}->{total}
);
# Perfdata buffer/cached/slab
if (defined($self->{result_values}->{buffer})) {
$self->{output}->perfdata_add(
nlabel => 'memory.buffer.bytes',
unit => 'B',
value => $self->{result_values}->{buffer},
min => 0
);
}
if (defined($self->{result_values}->{cached})) {
$self->{output}->perfdata_add(
nlabel => 'memory.cached.bytes',
unit => 'B',
value => $self->{result_values}->{cached},
min => 0
);
}
if (defined($self->{result_values}->{slab})) {
$self->{output}->perfdata_add(
nlabel => 'memory.slab.bytes',
unit => 'B',
value => $self->{result_values}->{slab},
min => 0
);
}
}
sub custom_memory_threshold {
my ($self, %options) = @_;
# Vérifier le seuil en pourcentage
my $exit_prct = $self->{perfdata}->threshold_check(
value => $self->{result_values}->{prct_used},
threshold => [
{ label => 'critical-memory-usage-prct', exit_litteral => 'critical' },
{ label => 'warning-memory-usage-prct', exit_litteral => 'warning' }
]
);
# Vérifier le seuil en bytes libres
my $exit_free = $self->{perfdata}->threshold_check(
value => $self->{result_values}->{free},
threshold => [
{ label => 'critical-memory-usage-free', exit_litteral => 'critical' },
{ label => 'warning-memory-usage-free', exit_litteral => 'warning' }
]
);
# === LOGIQUE DE ET ===
# CRITICAL si au moins un des deux est CRITICAL
if ($exit_prct eq 'critical' || $exit_free eq 'critical') {
return 'critical';
}
# WARNING seulement si LES DEUX sont WARNING
if ($exit_prct eq 'warning' && $exit_free eq 'warning') {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In custom_memory_threshold, 'warning' is reachable only if both prct and free are warning; when only one threshold type is configured, warning can never trigger, contradicting the mode’s single-threshold behavior.

Details

✨ AI Reasoning
​​1) The new logic is intended to support using percentage thresholds alone or free-bytes thresholds alone.
​2) The warning branch requires both threshold evaluations to be warning at the same time.
​3) If only one threshold type is configured, the other evaluation cannot become warning, so this branch cannot be satisfied.
​4) This creates a definite control-flow contradiction with the advertised behavior and makes warning alerts impossible in those valid configurations.

🔧 How do I fix it?
Trace execution paths carefully. Ensure precondition checks happen before using values, validate ranges before checking impossible conditions, and don't check for states that the code has already ruled out.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

return 'warning';
}
# Sinon OK (au moins un des deux est OK)
return 'ok';
}
sub custom_memory_output {
my ($self, %options) = @_;

return sprintf(
'Ram total: %s %s used (-%s): %s %s (%.2f%%) free: %s %s (%.2f%%) available: %s %s (%.2f%%)',
$self->{perfdata}->change_bytes(value => $self->{result_values}->{total}),
Expand All @@ -37,14 +129,11 @@ sub custom_memory_output {
$self->{perfdata}->change_bytes(value => $self->{result_values}->{free}),
$self->{result_values}->{prct_free},
$self->{perfdata}->change_bytes(value => $self->{result_values}->{available}),
$self->{result_values}->{prct_available},

$self->{result_values}->{prct_available}
);
}

sub custom_swap_output {
my ($self, %options) = @_;

return sprintf(
'Swap total: %s %s used: %s %s (%.2f%%) free: %s %s (%.2f%%)',
$self->{perfdata}->change_bytes(value => $self->{result_values}->{total}),
Expand All @@ -54,85 +143,23 @@ sub custom_swap_output {
$self->{result_values}->{prct_free}
);
}

sub set_counters {
my ($self, %options) = @_;

$self->{maps_counters_type} = [
{ name => 'memory', type => 0, skipped_code => { -10 => 1 } },
{ name => 'swap', type => 0, skipped_code => { -10 => 1 } }
];

$self->{maps_counters}->{memory} = [
$self->{maps_counters}->{memory} = [
{ label => 'memory-usage', nlabel => 'memory.usage.bytes', set => {
key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
closure_custom_output => $self->can('custom_memory_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'total', unit => 'B' }
]
}
},
{ label => 'memory-usage-free', nlabel => 'memory.free.bytes', display_ok => 0, set => {
key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
closure_custom_output => $self->can('custom_memory_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'total', unit => 'B' }
]
}
},
{ label => 'memory-usage-prct', nlabel => 'memory.usage.percentage', display_ok => 0, set => {
key_values => [ { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' } ],
closure_custom_output => $self->can('custom_memory_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'memory-available', nlabel => 'memory.available.bytes', display_ok => 0, set => {
key_values => [ { name => 'available' }, { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'prct_available' } ],
closure_custom_output => $self->can('custom_memory_output'),
perfdatas => [
{ template => '%d', min => 0, max => 'total', unit => 'B' }
]
}
},
{ label => 'memory-available-prct', nlabel => 'memory.available.percentage', display_ok => 0, set => {
key_values => [ { name => 'prct_available' }, { name => 'prct_used' }, { name => 'used' }, { name => 'free' }, { name => 'prct_free' }, { name => 'total' }, { name => 'used_desc' }, { name => 'available' } ],
key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' },
{ name => 'total' }, { name => 'used_desc' }, { name => 'available' }, { name => 'prct_available' },
{ name => 'buffer' }, { name => 'cached' }, { name => 'slab' } ],
closure_custom_output => $self->can('custom_memory_output'),
perfdatas => [
{ template => '%.2f', min => 0, max => 100, unit => '%' }
]
}
},
{ label => 'buffer', nlabel => 'memory.buffer.bytes', set => {
key_values => [ { name => 'buffer' } ],
output_template => 'buffer: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%d', min => 0, unit => 'B' }
]
}
},
{ label => 'cached', nlabel => 'memory.cached.bytes', set => {
key_values => [ { name => 'cached' } ],
output_template => 'cached: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%d', min => 0, unit => 'B' }
]
}
},
{ label => 'slab', nlabel => 'memory.slab.bytes', set => {
key_values => [ { name => 'slab' } ],
output_template => 'slab: %s %s',
output_change_bytes => 1,
perfdatas => [
{ template => '%d', min => 0, unit => 'B' }
]
closure_custom_perfdata => $self->can('custom_memory_perfdata'),
closure_custom_threshold_check => $self->can('custom_memory_threshold')
}
},
}
];

$self->{maps_counters}->{swap} = [
{ label => 'swap', nlabel => 'swap.usage.bytes', set => {
key_values => [ { name => 'used' }, { name => 'free' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
Expand All @@ -146,8 +173,7 @@ sub set_counters {
key_values => [ { name => 'free' }, { name => 'used' }, { name => 'prct_used' }, { name => 'prct_free' }, { name => 'total' } ],
closure_custom_output => $self->can('custom_swap_output'),
perfdatas => [
{ label => 'swap_free', template => '%d', min => 0, max => 'total',
unit => 'B', cast_int => 1 }
{ label => 'swap_free', template => '%d', min => 0, max => 'total', unit => 'B', cast_int => 1 }
]
}
},
Expand All @@ -161,38 +187,49 @@ sub set_counters {
}
];
}

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 => {
'swap' => { name => 'check_swap' },
'warning:s' => { name => 'warning', redirect => 'warning-memory-usage-percentage' },
'critical:s' => { name => 'critical', redirect => 'critical-memory-usage-percentage' }
'swap' => { name => 'check_swap' },
'warning-memory-usage-prct:s' => { name => 'warning_memory_usage_prct' },
'critical-memory-usage-prct:s' => { name => 'critical_memory_usage_prct' },
'warning-memory-usage-free:s' => { name => 'warning_memory_usage_free' },
'critical-memory-usage-free:s' => { name => 'critical_memory_usage_free' }
});

return $self;
}

sub check_options {
my ($self, %options) = @_;
$self->SUPER::check_options(%options);
# Validation des seuils
if (defined($self->{option_results}->{warning_memory_usage_prct}) && $self->{option_results}->{warning_memory_usage_prct} ne '') {
$self->{perfdata}->threshold_validate(label => 'warning-memory-usage-prct', value => $self->{option_results}->{warning_memory_usage_prct});
}
if (defined($self->{option_results}->{critical_memory_usage_prct}) && $self->{option_results}->{critical_memory_usage_prct} ne '') {
$self->{perfdata}->threshold_validate(label => 'critical-memory-usage-prct', value => $self->{option_results}->{critical_memory_usage_prct});
}
if (defined($self->{option_results}->{warning_memory_usage_free}) && $self->{option_results}->{warning_memory_usage_free} ne '') {
$self->{perfdata}->threshold_validate(label => 'warning-memory-usage-free', value => $self->{option_results}->{warning_memory_usage_free});
}
if (defined($self->{option_results}->{critical_memory_usage_free}) && $self->{option_results}->{critical_memory_usage_free} ne '') {
$self->{perfdata}->threshold_validate(label => 'critical-memory-usage-free', value => $self->{option_results}->{critical_memory_usage_free});
}
}
sub check_rhel_version {
my ($self, %options) = @_;

$self->{rhel_71} = 0;
return if ($options{stdout} !~ /(?:Redhat|CentOS|Red[ \-]Hat).*?release\s+(\d+)\.(\d+)/mi);
$self->{rhel_71} = 1 if ($1 >= 8 || ($1 == 7 && $2 >= 1));
}

sub manage_selection {
my ($self, %options) = @_;

my ($stdout) = $options{custom}->execute_command(
command => 'cat',
command_options => '/proc/meminfo /etc/redhat-release 2>&1',
no_quit => 1
);

# Buffer can be missing. In Openvz container for example.
my $buffer_used = 0;
my $available = 0;
Expand All @@ -216,23 +253,18 @@ sub manage_selection {
$available = $1 * 1024;
}
}

if (!defined($total_size) || !defined($cached_used) || !defined($free)) {
$self->{output}->add_option_msg(short_msg => 'Some informations missing.');
$self->{output}->option_exit();
}

$self->check_rhel_version(stdout => $stdout);

my $physical_used = $total_size - $free;
my $nobuf_used = $physical_used - $buffer_used - $cached_used;
if ($self->{rhel_71} == 1) {
$nobuf_used -= $slab_used if (defined($slab_used));
}

my $used_desc = 'buffers/cache';
$used_desc .= '/slab' if ($self->{rhel_71} == 1 && defined($slab_used));

$self->{memory} = {
total => $total_size,
used => $nobuf_used,
Expand All @@ -242,12 +274,10 @@ sub manage_selection {
used_desc => $used_desc,
available => $available,
prct_available => $available * 100 / $total_size,

buffer => $buffer_used,
cache => $cached_used,
cached => $cached_used,
slab => $slab_used
};

if (defined($self->{option_results}->{check_swap}) &&
defined($swap_total) && $swap_total > 0) {
$self->{swap} = {
Expand All @@ -259,32 +289,25 @@ sub manage_selection {
};
}
}

1;

__END__

=head1 MODE

Check physical memory (need '/proc/meminfo' file).

Command used: cat /proc/meminfo /etc/redhat-release 2>&1

Check Linux memory with AND logic for combined thresholds.
=over 8

=item B<--warning-memory-usage-prct>
Warning threshold for memory usage in percentage.
=item B<--critical-memory-usage-prct>
Critical threshold for memory usage in percentage.
=item B<--warning-memory-usage-free>
Warning threshold for free memory in bytes (range format: 0:value means alert if below value).
=item B<--critical-memory-usage-free>
Critical threshold for free memory in bytes (range format: 0:value means alert if below value).
=item B<--swap>

Check swap also.

=item B<--warning-*> B<--critical-*>

Thresholds.
Can be: 'memory-usage' (B), 'memory-usage-free' (B), 'memory-usage-prct' (%),
'memory-available' (B), 'memory-available-prct' (%),
'swap' (B), 'swap-free' (B), 'swap-prct' (%),
'buffer' (B), 'cached' (B), 'slab' (B).

Check swap usage.
=back

=cut

=head1 THRESHOLD LOGIC
This mode uses AND logic for combined thresholds:
- CRITICAL: if at least one threshold (prct OR free) is CRITICAL
- WARNING: only if BOTH thresholds (prct AND free) are WARNING
- OK: if at least one threshold is OK
=cut