Last time: Adding info from the past. This time: Looking into the future. As Munin is one of the ravens of Odin, one of the Norse gods, this script is called Skuld, the future Norne, one of the three female destiny deities from Norse mythology
Using rrdtool, perl and some simple statistical analysis, the linear least squares method, we try to predict when a filesystem reaches 100%. Very useful for system administrators.
Script can be downloaded from http://users.linpro.no/ingvar/munin/skuld.pl.txt
UPDATE: An even more flashy version, with optional --time and --value available at above URL.
#!/usr/bin/perl
#
# skuld - a perl script looking into the future
#
# This software is covered by the GNU General Public License version 2 or later.
# See http://www.gnu.org/copyleft/gpl.html for details.
#
use strict;
use Statistics::Descriptive;
unless (defined $ARGV[0]) {
print "usage: skuld some_munin_df_file.rrd\n";
exit 1;
}
my $debug=0;
my $rrdtool="/usr/bin/rrdtool";
my $rrd=$ARGV[0];
my $type="";
my $name="";
my @dayx = (); my $dayy = Statistics::Descriptive::Full->new();
my @weekx = (); my $weeky = Statistics::Descriptive::Full->new();
my @monthx= (); my $monthy= Statistics::Descriptive::Full->new();
my @yearx = (); my $yeary = Statistics::Descriptive::Full->new();
open (DUMP,"LANG=C $rrdtool dump $rrd |")
or die "Unable to open a pipe from $rrdtool dump, $!";
while (<DUMP>)
{
# Search for correct name
if ( /\<ds\>/ ) {
while ( <DUMP> ) {
/\<name\> (\w+) \<\/name\>/ and $name=$1;
last if /\<\/ds\>/
}
unless ( $name eq "42" ) {
print "No munin rrd file\n";
exit 2;
}
}
if ( /\<rra\>/ ) {
while ( <DUMP> ) {
# Only parse MAX values
if ( /\<cf\> MAX \<\/cf\>/ ) {
my $in=<DUMP>;
if ( $in =~ /\<pdp_per_row\> (\d+) \<\/pdp_per_row\>/ ) {
my $pdp=$1;
if ( $pdp == 1 ) { $type = "d"; print "day\n" if $debug; }
elsif ( $pdp == 6 ) { $type = "w"; print "week\n" if $debug; }
elsif ( $pdp == 24 ) { $type = "m"; print "month\n" if $debug; }
elsif ( $pdp == 288 ) { $type = "y"; print "year\n" if $debug; }
}
else { $type=""; }
}
if ( /\<database\>/ and ! $type eq "" ) {
while (<DUMP>) {
if ( /\<\!\-\-.+ \/ (\d+) \-\-\> \<row\>\<v\> (\d+\.\d+e.\d+) \<\/v\>\<\/row>$/ ) {
print "type: $type, time=$1, val=$2\n" if $debug;
if ( $type eq "d" ) { push @dayx, ($1); $dayy->add_data($2) };
if ( $type eq "w" ) { push @weekx, ($1); $weeky->add_data($2) };
if ( $type eq "m" ) { push @monthx,($1); $monthy->add_data($2) };
if ( $type eq "y" ) { push @yearx, ($1); $yeary->add_data($2) };
}
last if /\<\/database\>/;
}
}
last if /\<\/rra\>/;
}
}
}
my ($dq, $dm, $dr, $drms)=$dayy->least_squares_fit(@dayx);
my ($wq, $wm, $wr, $wrms)=$weeky->least_squares_fit(@weekx);
my ($mq, $mm, $mr, $mrms)=$monthy->least_squares_fit(@monthx);
my ($yq, $ym, $yr, $yrms)=$yeary->least_squares_fit(@yearx);
print "Daily forecast: Passes 100% at ";
print localtime((100-$dq)/$dm) . "\n";
print "Weekly forecast: Passes 100% at ";
print localtime((100-$wq)/$wm) . "\n";
print "Monthly forecast: Passes 100% at ";
print localtime((100-$mq)/$mm) . "\n";
print "Yearly forecast: Passes 100% at ";
print localtime((100-$yq)/$ym) . "\n";