- Repository
- Munin (2.0)
- Last change
- 2020-11-21
- Graph Categories
- Family
- auto
- Capabilities
- Keywords
- Language
- Perl
- License
- GPL-2.0-only
sensors_
Name
sensors_ - Wildcard-plugin to monitor information from temperature, voltage, and fan speed sensors.
Configuration
The possible wildcard values are the follwing: fan, temp, volt. So you would create symlinks to this plugin called sensors_fan, sensors_temp, and sensors_volt.
The plugins needs the following components configured:
- i2c and lm_sensors modules installed and loaded.
- sensors program installed and in path.
Note: Sensor names are read from the output of the sensors program. Change them in /etc/sensors.conf if you don’t like them.
[sensors_*]
env.sensors - Override default sensors program path
env.ignore_temp<n> - Temperature <n> will not be plotted
env.ignore_fan<n> - Fan <n> will not be plotted
env.ignore_volt<n> - Voltage <n> will not be plotted
env.fan_warn_percent - Percentage over mininum for warning
env.volt_warn_percent - Percentage over mininum/under maximum for warning
Author
Unknown author
License
GPLv2
Magic Markers
#%# family=auto
#%# capabilities=autoconf suggest
#!@@PERL@@ -w
# -*- perl -*-
=head1 NAME
sensors_ - Wildcard-plugin to monitor information from temperature,
voltage, and fan speed sensors.
=head1 CONFIGURATION
The possible wildcard values are the follwing: fan, temp, volt. So you
would create symlinks to this plugin called sensors_fan, sensors_temp,
and sensors_volt.
The plugins needs the following components configured:
=over
=item i2c and lm_sensors modules installed and loaded.
=item sensors program installed and in path.
=back
Note: Sensor names are read from the output of the sensors program.
Change them in /etc/sensors.conf if you don't like them.
[sensors_*]
env.sensors - Override default sensors program path
env.ignore_temp<n> - Temperature <n> will not be plotted
env.ignore_fan<n> - Fan <n> will not be plotted
env.ignore_volt<n> - Voltage <n> will not be plotted
env.fan_warn_percent - Percentage over mininum for warning
env.volt_warn_percent - Percentage over mininum/under maximum for warning
=head1 AUTHOR
Unknown author
=head1 LICENSE
GPLv2
=head1 MAGIC MARKERS
#%# family=auto
#%# capabilities=autoconf suggest
=cut
use strict;
$ENV{'LANG'} = "C"; # Force parsable output from sensors.
$ENV{'LC_ALL'} = "C"; # Force parsable output from sensors.
my $SENSORS = $ENV{'sensors'} || 'sensors';
# Example outputs from sensors & matching regex parts:
# Fan output example from sensors:
# --------------------------------
# Case Fan: 1268 RPM (min = 3750 RPM, div = 8) ALARM
# CPU Fan: 0 RPM (min = 1171 RPM, div = 128) ALARM
# Aux Fan: 0 RPM (min = 753 RPM, div = 128) ALARM
# fan4: 3375 RPM (min = 774 RPM, div = 8)
# fan5: 0 RPM (min = 1054 RPM, div = 128) ALARM
#
# ^^^^ ^^^^ ^^^^
# $+{label} $+{value} $+{threshold1}
#
# --------------------------------
#
# Temperature output example from sensors:
# * Note that the degree character is replaced by a space, as we are running
# with LC_ALL=C and LANG=C.
# ---------------------------------------
# Sys Temp: +41.0 C (high = -128.0 C, hyst = +24.0 C) ALARM sensor = thermistor
# CPU Temp: +40.5 C (high = +80.0 C, hyst = +75.0 C) sensor = thermistor
# AUX Temp: +39.0 C (high = +80.0 C, hyst = +75.0 C) sensor = thermistor
#
# ^^^^^^^^ ^^ ^^ ^^
# $+{label} $+{value} $+{threshold1} $+{threshold2}
#
# ---------------------------------------
#
# Voltage output example from sensors:
# ------------------------------------
#
# VCore: +1.09 V (min = +0.00 V, max = +1.74 V)
# in1: +12.14 V (min = +10.51 V, max = +2.38 V) ALARM
#
# ^^^ ^^^ ^^^ ^^
# $+{label} $+{value} $+{threshold1} $+{threshold2}
#
#
# ------------------------------------
my %config = (
fan => {
regex => qr/
^ # String must start with:
(?<label>[^:\n]*) # Match any non-whitespace char, except ':' and new line
\s* # Zero or more spaces followed by
: # : character
\s* # Zero or more spaces followed by
\+? # Zero or one '+' char. Note: This might not be needed
# as FAN speeds don't have + signs in front of them.
# This can be probably be removed as the
# sensors program never prints a + char in front of
# RPM values. Verified in lm-sensors-3-3.1.2
(?<value>\d+) # Match one or more digits (Match current value)
\s # Exactly one space followed by
(?: # a optional group, which might contain the minimum value:
RPM.*? # RPM string, and then any chars (but no newlines)
(?<threshold1>\d+) # Match one or more digits (Match minimum value)
\s # Exactly one space followed by
)? # end of optional group
RPM # RPM string
/xm, # Use extended regular expressions and treat string as multiple lines.
title => 'Fans',
vlabel => 'RPM',
print_threshold => \&fan_threshold,
graph_args => '--base 1000 -l 0'
},
temp => {
regex => qr/
^ # String must start with:
(?<label>[^:\n]*) # Match any non-whitespace char, except ':' and new line
\s* # Zero or more spaces followed by
: # ':' character
\s* # Zero or more spaces followed by
\+? # Zero or one '+' char followed by
(?<value>-? # Match zero or one '-' char
\d+ # Match one or more digits
# (Match current value) followed by
(?:\.\d+)?)# Zero or one match of '.' char followed by one or more
# digits. '?:' means it is not a numbered capture group.
[°\s*] # Match degree, space, or asterisk char
C # Match 'C' char
(?: # >>>>Match the following statement zero or one time.
# '?:' means it is not a numbered capture group.
\s+ # One or more space followed by
\( # '(' char
(?:high|limit|crit) # 'high' or 'limit' or 'crit' string.
# '?:' means it is not a numbered capture group.
\s*=\s* # Match zero or more spaces and then '=' char and then
# zero or more spaces, followed by
\+? # Zero or one '+' char
(?<threshold1>\d+ # Match one or more digits
# (Match high value) followed by
(?:\.\d+)?)# Zero or one match of '.' char followed by one or more
# digits. '?:' means it is not a numbered capture group.
[°\s*] # Match degree, space, or asterisk char
C,?\s* # 'C' followed by optional comma and zero or more spaces
(?: # >>>Match the following non-capture group zero or more times
(?:crit|hyst(?:eresis)?) # 'crit' or 'hyst' string followed by optional 'eresis' string.
# '?:' means it is not a numbered capture group.
\s*=\s* # Match zero or more spaces and then '=' char and then
# zero or more spaces, followed by
\+? # Zero or one '+' char
(?<threshold2>\d+ # Match one or more digits
# (Match hyst value) followed by
(?:\.\d+)?)# Zero or one match of '.' char followed by one or more
# digits. '?:' means it is not a numbered capture group.
[°\s*]C # Match degree, space, or asterisk char, and then 'C' char
)? # >>>end of group
\) # ')' char
)? # >>>>end of group
/xm, # Use extended regular expressions and treat string as multiple lines.
title => 'Temperatures',
vlabel => 'degrees Celsius',
print_threshold => \&temp_threshold,
graph_args => '--base 1000 -l 0'
},
volt => {
regex => qr/
^ # String must start with:
(?<label>[^:\n]*) # Match any non-whitespace char, except ':' and new line
# one space in front of them.
\s*:\s* # Zero or more spaces followed by ':' and
# zero or more spaces followed by
\+? # Zero or one '+' char followed by
(?<value>-? # Match zero or one '-' char
\d+ # Match one or more digits
# (match the current voltage) followed by
(?:\.\d+)?) # Zero or one match of '.' char followed by one or more digits.
# '?:' means it is not a numbered capture group.
\s(?<unit_prefix_value>[m])?V # one space, 'V' char
(?: # >>>>Match the following statement.
# '?:' means it is not a numbered capture group.
\s+ # One or more space followed by
\( # '(' char
min # Match min string
\s*=\s* # Match zero or more spaces and then '=' char and
# then zero or more spaces, followed by
\+? # Zero or one '+' char
(?<threshold1>-? # Match zero or one '-' char
\d+ # Match one or more digits
# (Match the minimum value) followed by
(?:\.\d+)?) # Zero or one match of '.' char followed by one or more
# digits. '?:' means it is not a numbered capture group.
\s(?<unit_prefix_threshold1>[m])?V,\s* # One space char, 'V,' string followed by zero or
# more spaces
max # Match 'max' string
\s*=\s* # Match zero or more spaces and then '=' char and
# then zero or more spaces, followed by
\+? # Zero or one '+' char
(?<threshold2>-? # Match zero or one '-' char
\d+ # Match one or more digits
# (Match the max value) followed by
(?:\.\d+)?) # Zero or one match of '.' char followed by one or more digits.
# '?:' means passive group (does not match)
\s(?<unit_prefix_threshold2>[m])?V # one space, 'V' char
\) # ')' char
)? # >>>>end of statement
/xm, # Use extended regular expressions and treat string as multiple lines.
title => 'Voltages',
vlabel => 'Volt',
print_threshold => \&volt_threshold,
graph_args => '--base 1000 --logarithmic'
},
power => {
regex => qr/
^ # String must start with:
(?<label>[^:\n]*) # Match any non-whitespace char, except ':' and new line
# one space in front of them.
\s*:\s* # Zero or more spaces followed by ':' and
# zero or more spaces followed by
(?<value>\d+ # Match one or more digits
# (match the power) followed by
(?:\.\d+)?) # Zero or one match of '.' char followed by one or more digits.
# '?:' means it is not a numbered capture group.
\sW # one space, 'W' char
(?: # >>>>Match the following statement.
# '?:' means it is not a numbered capture group.
\s+ # One or more space followed by
\( # '(' char
crit # Match crit string
\s*=\s* # Match zero or more spaces and then '=' char and
# then zero or more spaces, followed by
(?<threshold1>\d+ # Match one or more digits
# (Match the minimum value) followed by
(?:\.\d+)?) # Zero or one match of '.' char followed by one or more
# digits. '?:' means it is not a numbered capture group.
\sW\s* # One space char, 'W' character followed by zero or
# more spaces
\) # ')' char
)? # >>>>end of statement
/xm, # Use extended regular expressions and treat string as multiple lines.
title => 'Power',
vlabel => 'Watts',
print_threshold => \&power_threshold,
graph_args => '--base 1000 --logarithmic'
},
);
if ( defined $ARGV[0] and $ARGV[0] eq 'autoconf' ) {
# Now see if "sensors" can run
my $text = `$SENSORS 2>/dev/null`;
if ($?) {
if ($? == -1) {
print "no (program $SENSORS not found)\n";
} else {
print "no (program $SENSORS died)\n";
}
exit 0;
}
unless ($text =~ /[°\s*]C/) {
print "no (no temperature readings)\n";
exit 0;
}
print "yes\n";
exit 0;
}
if (defined $ARGV[0] and $ARGV[0] eq 'suggest') {
my $text = `$SENSORS 2>/dev/null`;
foreach my $func (keys %config) {
print $func, "\n" if $text =~ $config{$func}->{regex};
}
exit;
}
$0 =~ /sensors_(.+)*$/;
my $func = $1;
exit 2 unless defined $func;
if ( defined $ARGV[0] and $ARGV[0] eq 'config' ) {
print "graph_title $config{$func}->{title}\n";
print "graph_vlabel $config{$func}->{vlabel}\n";
print "graph_args $config{$func}->{graph_args}\n";
print "graph_category sensors\n";
my $text = `$SENSORS`;
my $sensor = 1;
while ($text =~ /$config{$func}->{regex}/g) {
print "$func$sensor.label $+{label}\n";
my $threshold1 = convert_unit_value($+{threshold1}, $+{unit_prefix_threshold1});
my $threshold2 = convert_unit_value($+{threshold2}, $+{unit_prefix_threshold2});
$config{$func}->{print_threshold}->($func.$sensor, $threshold1, $threshold2);
print "$func$sensor.graph no\n" if exists $ENV{"ignore_$func$sensor"};
$sensor++;
}
exit 0;
}
my $text = `$SENSORS`;
my $sensor = 1;
while ($text =~ /$config{$func}->{regex}/g) {
my $value = convert_unit_value($+{value}, $+{unit_prefix_value});
print "$func$sensor.value $value\n";
$sensor++;
}
sub fan_threshold {
my $name = shift;
my $min = shift;
my $warn_percent = exists $ENV{fan_warn_percent} ? $ENV{fan_warn_percent} : 5;
return unless defined $min;
printf "$name.warning %d:\n", $min * (100 + $warn_percent) / 100;
printf "$name.critical %d:\n", $min;
}
sub temp_threshold {
my $name = shift;
my $max = shift;
my $min = shift;
if (defined($min) and defined($max) and $min > $max) {
($min, $max) = ($max, $min);
}
printf "$name.warning $min\n" if $min;
printf "$name.critical $max\n" if $max;
}
sub volt_threshold {
my $name = shift;
my $min = shift;
my $max = shift;
my $warn_percent = exists $ENV{volt_warn_percent} ? $ENV{volt_warn_percent} : 20;
return unless defined ($min && $max);
my $diff = $max - $min;
my $dist = $diff * $warn_percent / 100;
printf "$name.warning %.2f:%.2f\n", $min + $dist, $max - $dist;
printf "$name.critical $min:$max\n";
}
sub power_threshold {
my $name = shift;
my $crit = shift;
my $warn_percent = exists $ENV{watt_warn_percent} ? $ENV{watt_warn_percent} : 20;
return unless defined ($crit);
printf "$name.warning :%.2f\n", $crit * (100-$warn_percent)/100;
printf "$name.critical :$crit\n";
}
sub convert_unit_value {
my $value = shift;
my $unit_prefix = shift;
if (defined $unit_prefix) {
if ($unit_prefix eq "m") {
$value /= 1000;
} else {
warn "Unknown value prefix: '$unit_prefix'";
}
}
return $value;
}
# vim:syntax=perl