Tag Archives: munin

Raspberry Pi sensors – Munin graphing plugin

I love Munin!  I’ve finally got one of the Raspberry Pi’s to be reasonably stable, so I’ve set up a munin-node on it.  The standard Linux sensord stuff doesn’t run on the arm core, so I had assumed I wouldn’t be able to see any exciting temperature graphs, but I was wrong!

The Raspberry Pi Debian image includes a command called vcgencmd, which allows root to interrogate various settings and measurements.  That includes the temperature, clock frequencies and voltages across various components.

So I’ve knocked up a quick plugin for Munin which gathers that stuff and graphs it.  You can get it over at GitHub here.  The current code looks like this (but the GitHub copy will be most up-to-date),

#!/bin/bash
# -*- sh -*-

: << =cut

=head1 NAME

pisense_ - Wildcard-plugin to monitor Raspberry Pi sensors (temp, volts, clock speed)

=head1 CONFIGURATION

This plugin needs to be run as root for vcgencmd to work.

[pisense_*]
user root

=head2 ENVIRONMENT VARIABLES

This plugin does not use environment variables.

=head2 WILDCARD PLUGIN

This is a wildcard plugin. To specify if you want temperature,
clock speed or volts, link this file to _volt, _temp
or _clock.

For example,

ln -s /usr/share/munin/plugins/pisense_ \
/etc/munin/plugins/pisense_clock

will monitor the clock speeds of your pi.

=head1 BUGS

None known.

=head1 NOTES

This plugin is shamelessley based on the ip_ plugin (structure).

=head1 MAGIC MARKERS

#%# family=auto
#%# capabilities=autoconf suggest

=head1 AUTHOR

Tony (tony@darkstorm.co.uk).

=head1 LICENSE

It's yours, do with it what you like.

=cut

. $MUNIN_LIBDIR/plugins/plugin.sh

sensor=${0##*/pisense_}

if [[ "$1" == "autoconf" ]]; then
    if ! /opt/vc/bin/vcgencmd firmware >/dev/null 2>/dev/null; then
        echo "no (could not run /opt/vc/bin/vcgencmd as user $(whoami))"
        exit 0
    else
        echo yes
        exit 0
    fi
fi

# this is flawed, vcgencmd always returns with RC 0. Needs expanding.
if [[ "$1" == "suggest" ]]; then
    if /opt/vc/bin/vcgencmd measure_temp >/dev/null 2>/dev/null; then
        echo temp
    fi
    if /opt/vc/bin/vcgencmd measure_volts >/dev/null 2>/dev/null; then
        echo volt
    fi
    if /opt/vc/bin/vcgencmd measure_clock core >/dev/null 2>/dev/null; then
        echo clock
    fi
    exit 0
fi

if [[ "$1" == "config" ]]; then

    if [[ "$sensor" == "temp" ]]
    then
        echo "graph_title Raspberry Pi core temp"
        echo "graph_args --base 1000"
        echo "graph_vlabel dgrees Celsius"
        echo "graph_category sensors"
        echo "temp.label Core Temperature"
        echo "temp.min 0"
    fi
    if [[ "$sensor" == "clock" ]]
    then
        echo "graph_title Raspberry Pi clock frequencies"
        echo "graph_args --base 1000"
        echo "graph_vlabel herz"
        echo "graph_category sensors"
        for clock in arm core h264 isp v3d uart pwm emmc pixel vec hdmi dpi
        do
           echo "clock$clock.label $clock clock Frequency"
           echo "clock$clock.min 0"
           echo "clock$clock.type GAUGE"
        done
    fi
    if [[ "$sensor" == "volt" ]]
    then
        echo "graph_title Raspberry Pi voltages"
        echo "graph_args --base 1000"
        echo "graph_vlabel volts"
        echo "graph_category sensors"
        for volt in core sdram_c sdram_i sdram_p
        do
           echo "volt$volt.label $volt voltage"
           echo "volt$volt.min 0"
           echo "volt$volt.type GAUGE"
        done
    fi

    exit 0
fi;

if [[ "$sensor" == "temp" ]]
then
    temp=$(/opt/vc/bin/vcgencmd measure_temp | awk -F"=" '{print $2}' | awk -F"'" '{print $1}')
    echo "temp.value $temp"
fi
if [[ "$sensor" == "clock" ]]
then
    for clock in arm core h264 isp v3d uart pwm emmc pixel vec hdmi dpi
    do
       clockval=$(/opt/vc/bin/vcgencmd measure_clock $clock | awk -F"=" '{print $2}')
       echo "clock$clock.value $clockval"
    done
fi
if [[ "$sensor" == "volt" ]]
then
   for volt in core sdram_c sdram_i sdram_p
   do
       voltage=$(/opt/vc/bin/vcgencmd measure_volts $volt | awk -F"=" '{print $2}' | tr -d "V")
       echo "volt$volt.value $voltage"
   done
fi

I’ll post some sample screenshots in a bit!

Graphing ngIRCd stats (users / servers / channels) with Munin

I run a little ngIRCd server (well two) for some friends to use (if you’re a friend, and you want to know more, mail me!)

I’m also addicted to numbers, and I’m always interested in monitoring the stuff I run, so I’ve written (adapted really) an existing script to track how many users are connected to the ngIRCd daemons using munin.

I’m hosting them on github (along with some other stuff I wrote, or am writing).  If you’re interested, you can check them out (hah, get it? I made a version control joke!) here.

Graphing INN2 stats through Munin

I run a news server (using INN2), and I graph the performance of the server on which it sits using Munin.  I used to use Cacti, but find Munin much, much easier to set up and get going, even if the interface isn’t quite as fancy.

Munin comes with a collection of regular plugins for graphing various linux services and system stats (such as Apache, MySQL, etc.) but it obviously can’t include everything.  I spent about 10 minutes seeing if anyone had written their own plugin for reporting on INN2 traffic stats, but couldn’t see anything.  So I rolled my own.  I tried this under Cacti and frankly, it was painful.  Not so much the script to gather the data, but getting the graphs actually up and running.

With Munin, it’s trivial.  You can read all about it here, which is where I worked out what I needed to do.

Disclaimer: My shell scripting is shockingly lazy, and my use of awk can be compared to someone playing an exquisite 400 year old violin with a hammer.  Sorry, but suck it up.

I use Munin on a Debian system installed, so the paths may vary depending on your setup.  You need to complete three steps.

1. Get your stats

Munin collects data every 300 seconds.  You need a script which can be run every 300 seconds to return data.  The output of your script should be,

datavariable.value actualvalue

where datavariable is the name you give your erm, data variable, value is the word value and actualvalue is the value you want to graph.  If you have several values, you just output them one line at a time,

datavariable.value actualvalue
datavariable_b.value actualvalue
datavariable_c.value actualvalue

Once you have a script which can output that, you’re most of the way there.

2. Describe your graph

What Munin actually graphs is based on what your script outputs when it is passed a command line parameter of config.  There are a bunch of values you should return which describe the graph, and then a bunch of values which describe the variables (i.e. the stuff above returned by your script), their format and how they should be put onto the graph.  Here’s a simple example,

echo 'graph_title CPU usage'
echo 'graph_vlabel %'
echo 'graph_scale no'
echo 'graph_info This graph shows how CPU time is spent.'
echo 'system.label system'
echo 'system.draw AREA'
echo 'system.min 0'

That would tell Munin to create a graph with the relevant title, to label the vertical axis with a single %, add a little bit of text and graph a single variable called system.  The system variable is labelled on the graph as system, is an AREA plot and has a minimum value of 0.

There’s plenty more you can do, which you can read about on the Munin site, but that’s the basic stuff, and should be returned when your script is called with config on the command line.  (By the way, your script can be anything executable, perl, shell, etc.)

3. Tell Munin to use your script

You should place your script (executable) in /etc/munin/plugins or a link to it.  You should also add various config options to /etc/munin/plugin-conf.d/munin-node which control how your script behaves.  I’m not covering that in detail here but it’s fairly obvious.

You then restart the munin agent, and bingo your stuff turns up.  Like magic.

INN2 Graphs

So, if you want to graph INN2 data, set INN2 to output stats (inn.conf and innfeed.conf) every 300 seconds (or quicker).  Then use the following two scripts, and entries in the munin-node config file (as appropriate for your installation).

inn2_incoming

#!/bin/sh
#
# Plugin to monitor inn2 status file, and report incoming traffic
#
# Require read permissions on the appropriate status file, and INN2 to be configured
# to update the file at or quicker than every 5 minutes.
#
#
#
# Parameters:
#
#       config   (required)
#       autoconf (optional - used by munin-config)
#
# Magick markers (optional):
#%# family=auto
#%# capabilities=autoconf
#
# config example for /etc/munin/plugin-conf.d/munin-node
#[inn2_incoming]
#user news
#group news
#env.logfile /var/log/news/inn_status.html
#

LOG=${logfile:-/var/log/news/inn_status}
CATEGORY=${category:-inn2}

if [ "$1" = "autoconf" ]; then
 if [ -r "$LOG" ]; then
 echo yes
 exit 0
 else
 echo no
 exit 1
 fi
fi

if [ "$1" = "config" ]; then

 echo 'graph_title INN2 incoming article stats'
 echo 'graph_args --base 1000 -l 0'
 echo 'graph_scale no'
 echo 'graph_vlabel article count'
 echo 'graph_category' $CATEGORY
 echo 'graph_period minute'

 echo 'offered.label Total articles offered'
 echo 'offered.type DERIVE'
 echo 'offered.min 0'
 echo 'accepted.label Articles accepted'
 echo 'accepted.type DERIVE'
 echo 'accepted.min 0'
 echo 'refused.label Articles refused'
 echo 'refused.type DERIVE'
 echo 'refused.min 0'
 echo 'rejected.label Articles rejected'
 echo 'rejected.type DERIVE'
 echo 'rejected.min 0'
 echo 'dupe.label Duplicate articles'
 echo 'dupe.type DERIVE'
 echo 'dupe.min 0'

 exit 0
fi

awk -v RS="" '/global/ {print $0}' < $LOG | awk -v RS="" -F":" '{printf "offered.value %d\naccepted.value %d\nrefused.value %d\nrejected.value %d\ndupe.value %d\n",$5,$7,$10,$13,$16}'

inn2_outgoing

#!/bin/sh
#
# Plugin to monitor inn2 status file, and report outgoing traffic
#
# Require read permissions on the appropriate status file, and INN2 to be configured
# to update the file at or quicker than every 5 minutes.
#
#
#
# Parameters:
#
#       config   (required)
#       autoconf (optional - used by munin-config)
#
# Magick markers (optional):
#%# family=auto
#%# capabilities=autoconf
#
# config example for /etc/munin/plugin-conf.d/munin-node
#[inn2_incoming]
#user news
#group news
#env.logfile /var/log/news/innfeed.status
#

LOG=${logfile:-/var/log/news/innfeed.status}
CATEGORY=${category:-inn2}

if [ "$1" = "autoconf" ]; then
        if [ -r "$LOG" ]; then
                echo yes
                exit 0
        else
                echo no
                exit 1
        fi
fi

if [ "$1" = "config" ]; then

        echo 'graph_title INN2 outgoing article stats'
        echo 'graph_args --base 1000 -l 0'
        echo 'graph_scale no'
        echo 'graph_vlabel article count'
        echo 'graph_category' $CATEGORY
        echo 'graph_period minute'

        echo 'offered.label Total articles offered'
        echo 'offered.type DERIVE'
        echo 'offered.min 0'
        echo 'accepted.label Articles accepted'
        echo 'accepted.type DERIVE'
        echo 'accepted.min 0'
        echo 'refused.label Articles refused'
        echo 'refused.type DERIVE'
        echo 'refused.min 0'
        echo 'rejected.label Articles rejected'
        echo 'rejected.type DERIVE'
        echo 'rejected.min 0'
        echo 'missing.label Missing articles'
        echo 'missing.type DERIVE'
        echo 'missing.min 0'
        echo 'deferred.label Deferred articles'
        echo 'deferred.type DERIVE'
        echo 'deferred.min 0'

        exit 0
fi

awk -v RS="" '/global \(process\)/ {print $0}' < $LOG | awk -v RS="" -F":" '{printf "offered.value %d\naccepted.value %d\nrefused.value %d\nrejected.value %d\nmissing.value %d\ndeferred.value %d\n",$5,$7,$9,$11,$13,$15}'

Obviously, depending on your version of INN2, you may have to tweak the awk entry to get the right values.

Lastly, put the following lines into /etc/munin/plugin-conf.d/munin-node

[inn2_incoming]
user news
group news
env.logfile /var/log/news/inn_status.html

[inn2_outgoing]
user news
group news
env.logfile /var/log/news/innfeed.status

And again, those values will need to change to match your distribution / INN2 setup.  Hope this helps someone.