#!@@PERL@@ -w
#
#
# Plugin for watching io-bound traffic (in number of operations) on disks.
#
# Usage: Link or copy into /etc/munin/plugins/
#
# Parameters:
#
#       config   (required)
#       autoconf (optional - used by munin-config)
#
# Magic markers (optional - used by munin-config and some installation 
# scripts):
# 
#%# family=contrib

use strict;

use IO::File;

use Storable qw(store retrieve);

# (c) 2004 - Per Andreas Buer
# GPL v2 Yadda yadda.

use constant STATEFILE => '@@PLUGSTATE@@/iostat-ios.state';


if ($ARGV[0] eq 'config') {
    print_config();
    exit;
}


my ($r, $old_r);

$r = get_ios();


($old_r) = get_state();



if ($old_r) {
    cmp_io($old_r, $r);
} else {
    print "No historic data present\n";
}

store_state( $r );


sub filter {
    my ($major, $minor, $tmpnam);
    return 0 if ($major ==   1); # RAM devices
    return 0 if ($major ==   9); # MD devices
    return 0 if ($major ==  58); # LVM devices
    return 0 if ($major == 254); # LVM2 devices
    return 0 if ($tmpnam =~ /part\d+$/);
    return 0 if ($tmpnam =~ /^\s*(?:sd|hd)[a-z]\d+\s*$/);

    return 1;
}



sub get_ios {
    my ($opt) = @_;

    my %R;
    my ($parts, $kernel);
    my @dev;
    
    if (-r "/proc/diskstats") {
        $kernel = 2.6;
        $parts = new IO::File("/proc/diskstats") || die();
    } else {
        $kernel = 2.4;
        $parts = new IO::File("/proc/partitions");
        die("kernel $kernel not supported yet\n");
    }

    unless ($parts) {
        print "Could not gather statistics\n";
        return undef;
    }

    while (<$parts>) {
         my ($maj, $min, $name, $rio, $rtime, $wio, $wtime);

         ($maj, $min, $name, 
          $rio, $rtime, $wio, $wtime) = 
           (split(/\s+/, $_ ))[1,2,3,4,7,8,11];

         next unless( defined($min) && defined($maj));
         next unless ($wio and $rio and $rtime and $wtime);

         next if ( filter($maj, $min, $name) == 0);

         $R{$maj}{$min}{rio}   =  $rio;
         $R{$maj}{$min}{rtime} =  $rtime;

         $R{$maj}{$min}{wio}   =  $wio;
         $R{$maj}{$min}{wtime} =  $wtime;
         push(@dev, "dev${maj}_${min}");
    }
    $parts->close();

    if ($opt) {
        return \@dev;
    } else {
        return \%R;
    }
}

sub print_config {

    print("graph_title IO Service time\n",
          "graph_args --base 1000\n",
          "graph_category disk\n",
          "graph_vlabel ms\n");

    for my $d ( @{ get_ios(1) } ) {
        print("$d.label $d\n",
              "$d.type GAUGE\n",
              "$d.draw LINE2\n");
    }
}


sub cmp_io {
    my ($old_io, $new_io) = @_;
    my %KEYS;

    for my $maj (sort keys %{$new_io} ) {
        for my $min (sort keys %{ $new_io->{$maj} } ) {
            my $rio_diff   = $$new_io{$maj}{$min}{rio}  - $$old_io{$maj}{$min}{rio};
            my $rtime_diff   = $$new_io{$maj}{$min}{rtime}  - $$old_io{$maj}{$min}{rtime};
            

            my $wio_diff   = $$new_io{$maj}{$min}{wio}  - $$old_io{$maj}{$min}{wio};
            my $wtime_diff   = $$new_io{$maj}{$min}{wtime}  - $$old_io{$maj}{$min}{wtime};
            
            my $dev = "dev${maj}_${min}";

            print("${dev}_rtime.value ", ($rtime_diff != 0) ? ($rio_diff / $rtime_diff) : 0, "\n",
                  "${dev}_wtime.value ", ($wtime_diff != 0) ? ($wio_diff / $wtime_diff) : 0, "\n",
                  );
        }
    }

}

sub store_state {
    my ($R) = @_;
    store($R, STATEFILE);
}

sub get_state {
    my ($R);
    return(undef) unless ( -r STATEFILE);
    $R = retrieve( STATEFILE );
    return($R);
}
