IT

ipmi-sensors and collectd

Introduction

Charting sensor data using collectd is a breeze, however the sensor names end up being hard to decipher on an AsRock X470D4U motherboard. Can you tell which one is the CPU Temperature ?

I decided to install the freeipmi library on Fedora to poke around and the output of ipmi-sensors is very readable:

Reading IPMI sensors

Initially I have tried configuring the ipmi plugin for collectd, but it would crash upon start: https://bugzilla.redhat.com/show_bug.cgi?id=1927022

Output could be parsed using a shell script plugged into the exec plugin, but the exec plugin tries to forbid running scripts as root; in case you need it (and ipmi-sensors requires root) then you have to add a new user to your system and force that user to have gid 0.

Reading IPMI sensor values with Perl

perl plugin does run scripts as root. Thus, I decided to write a small Perl script to parse the output and push the ‘Temperature’, ‘Fan’ and ‘Voltage’ sensor readings to Graphite. Perl (and Python) plugins do run their scripts are root.

It needs perl-Collectd package being installed (Fedora 33) as well as the freeipmi package (provides ipmi-sensors), then activating the perl plugin and configuration in collectd.conf to point to the script:

LoadPlugin perl
...
<Plugin perl>
        IncludeDir "/usr/lib64/collectd"
        BaseName "Collectd::Plugins"
#       EnableDebugger ""
        LoadPlugin IpmiSensors

        <Plugin IpmiSensors>
        </Plugin>
</Plugin>

The file needs to be saved to /usr/lib64/collectd/Collectd/Plugins/IpmiSensors.pm

# /usr/lib64/collectd/Collectd/Plugins/IpmiSensors.pm
#
# A Perl plugin script to help parsing ipmi-sensors output.
#
# Written by viulian
#
# This is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation; only version 2 of the License is applicable.

# Notes:
# - freeipmi library is needed (providing ipmi-sensors executable)
# - perl-Collectd.x86_64 must be installed

package Collectd::Plugin::IpmiSensors;

use strict;
use warnings;

use Collectd qw( :all );

# Variables

# This code is executed after loading the plugin to register it with collectd.
plugin_register (TYPE_LOG, 'IpmiSensors', 'my_log');
plugin_register (TYPE_NOTIF, 'IpmiSensors', 'my_notify');
plugin_register (TYPE_INIT, 'IpmiSensors', 'my_init');
plugin_register (TYPE_READ, 'IpmiSensors', 'my_read');
plugin_register (TYPE_WRITE, 'IpmiSensors', 'my_write');
plugin_register (TYPE_SHUTDOWN, 'IpmiSensors', 'my_shutdown');
plugin_register (TYPE_CONFIG, 'IpmiSensors', 'my_config');

# For each of the functions below see collectd-perl(5) for details about
# arguments and the like.

sub my_config {
        1;
} # my_config ()

# This function is called once upon startup to initialize the plugin.
sub my_init
{
        return 1;
} # my_init ()

# This function is called in regular intervals to collectd the data.
sub my_read
{
        my @stdout = qx/\/usr\/sbin\/ipmi-sensors --ignore-not-available-sensors --no-header-output --comma-separated-output/;

        for (@stdout) {
            chomp();
            my @spl = split(',', $_);
            # 1,3VSB,Voltage,3.38,V,'OK'

            my $type;
            if ($spl[2] eq 'Temperature') {
                    $type = 'temperature';
            } elsif ($spl[2] eq 'Voltage') {
                    $type = 'voltage';
            } elsif ($spl[2] eq 'Fan') {
                    $type = 'fanspeed';
            } else {
                    next; # not caring about anything else now; WARN may litter logs.
            }

            my $type_instance = $spl[1];
            # sanitize a bit (replace spaces and dots by _)
            $type_instance =~ s/\ /_/g;
            $type_instance =~ s/\./_/g;
            $type_instance = lc($type_instance);

            # ready to ship
            my $vl = {};
            $vl->{'values'} = [ $spl[3] ];
            $vl->{'plugin'} = 'ipmi-sensors';
            # $vl->{'plugin_instance'} = 'instance';
            $vl->{'type'} = $type;
            $vl->{'type_instance'} = $type_instance;

            plugin_dispatch_values($vl)
        }

        # A false return value indicates an error and the plugin will be skipped
        # for an increasing amount of time.
        return 1;
} # my_read ()

# This function is called after values have been dispatched to collectd.
sub my_write
{
        return 1;
} # my_write()

# This function is called before shutting down collectd.
sub my_shutdown
{
        return 1;
} # my_shutdown ()

# This function is called when plugin_log () has been used.
sub my_log
{
        return 1;
} # my_log ()

# This function is called when plugin_dispatch_notification () has been used
sub my_notify
{
        return 1;
} # my_notify ()

Results

As you can see below, the measurements reflect the name of the sensors and the whole thing is very much readable.

The chart with CPU and Motherboard temperatures:

An interesting thing to note is that it seems that the IPMI temperatures for the CPU are rounded up(or down) to 0.5C. I was able to identify the corresponding CPU sensors into the output of the sensors plugin: the value provided by the sensors plugin is much more accurate (sensors plugin output is in green, IPMI in blue):

Hope this is useful,

Leave a Reply