Code

tools/distclean: use git-clean when possible
[nagiosplug.git] / contrib-reporting / process_perfdata.pl
1 #!/usr/local/bin/perl -w
2 # author: Al Tobey <albert.tobey@priority-health.com>
3 # what:    process perfdata from Nagios and put it into RRD files
4 # license: GPL - http://www.fsf.org/licenses/gpl.txt
5 #
6 # Todo:
7 # * more documentation (POD) & comments
8 # * clean up a bit, make it more configurable - possibly a config file
10 use strict;
11 use lib qw( /opt/nagios/libexec );
12 use utils qw( %ERRORS );
13 use vars qw( $debug_file %data $debug $rrd_base $process_func $rrd_type );
14 use RRDs;
15 $debug_file = undef; #"/var/opt/nagios/perfdata.out";
16 $rrd_base = '/var/opt/nagios/rrds';
17 $process_func = \&process_multi;
18 $rrd_type = 'GAUGE';
19 $data{hostname}  = shift(@ARGV);
20 $data{metric}    = shift(@ARGV);
21 $data{timestamp} = shift(@ARGV);
22 $data{perfdata}  = join( " ", @ARGV ); $data{perfdata} =~ s/\s+/ /g;
24 # make sure there's data to work with
25 exit $ERRORS{OK} if ( !defined($data{hostname}) || !defined($data{metric})
26     || !defined($data{timestamp}) || !defined($data{perfdata})
27     || $data{perfdata} eq ' ' || $data{perfdata} eq '' );
29 if ( defined($debug_file) ) {
30     open( LOGFILE, ">>$debug_file" );
31     print LOGFILE "$data{hostname}\t$data{metric}\t$data{timestamp}\t$data{perfdata}\n\n";
32 }
34 # make sure host directory exists
35 if ( !-d "$rrd_base/$data{hostname}" ) {
36     mkdir( "$rrd_base/$data{hostname}" )
37         || warn "could not create host directory $rrd_base/$data{hostname}: $!";
38 }
39 our $rrd_dir = $rrd_base .'/'. $data{hostname};
41 # --------------------------------------------------------------------------- #
42 # do some setup based on the name of the metric
44 # host data
45 if ( $data{metric} eq "HOSTCHECK" ) {
46     $rrd_dir .= '/hostperf';
47 }
48 # processing disk information
49 elsif ( $data{metric} =~ /_DISK$/ ) {
50     $rrd_dir .= '/disk';
51 }
52 # network interface information
53 elsif ( $data{metric} =~ /^IFACE_/ ) {
54     $rrd_dir .= '/interfaces';
55     $rrd_type = [ 'COUNTER', 'COUNTER' ];
56 }
57 # process performance statistics
58 elsif ( $data{metric} =~ /_PROC$/ ) {
59     $rrd_dir .= '/processes';
60     $process_func = \&process_single;
61     $rrd_type = [ 'COUNTER', 'GAUGE' ];
62 }
63 # a resonable guess
64 elsif ( $data{perfdata} =~ /=/ ) {
65     $process_func = \&process_single;
66 }
67 # everything else
68 else {
69     $rrd_dir .= '/other';
70 }
72 # --------------------------------------------------------------------------- #
73 # call the proper processing function set up above (functions defined below)
74 our @processed = ( $process_func->() );
76 # --------------------------------------------------------------------------- #
78 if ( !-d $rrd_dir ) {
79     mkdir( $rrd_dir ) || warn "could not mkdir( $rrd_dir ): $!";
80 }
81 foreach my $datum ( @processed ) {
82     if ( defined($debug_file) ) {
83         print LOGFILE $datum->{rrd_name}, " = ", join('--',@{$datum->{data}}), "\n"
84     }
86     my $rrdfile = $rrd_dir.'/'.$datum->{rrd_name};
88     # create the RRD file if it doesn't already exist
89     if ( !-e $rrdfile ) {
90         # create a non-useful datasource title for each "part"
91         RRDs::create( $rrdfile, "-b", $data{timestamp}, "-s", 300, $process_func->($datum, 1),
92             "RRA:AVERAGE:0.5:1:600",
93             "RRA:MAX:0.5:1:600",
94             "RRA:AVERAGE:0.5:6:600",
95             "RRA:MAX:0.5:6:600",
96             "RRA:AVERAGE:0.5:24:600",
97             "RRA:MAX:0.5:24:600",
98             "RRA:AVERAGE:0.5:288:600",
99             "RRA:MAX:0.5:288:600"
100         );
101         if ( my $ERROR = RRDs::error ) { print "ERROR: $ERROR\n"; exit $ERRORS{UNKNOWN}; }
102     }
103     else {
104         # create a template to make sure data goes into the RRD as planned
105         if ( defined($debug_file) ) {
106             print LOGFILE "updating $rrdfile with data:\n\t",
107                 join(':', $data{timestamp}, @{$datum->{data}}), "\n";
108         }
109         # update the RRD file
110         RRDs::update( $rrdfile, '-t', $process_func->($datum),
111             join(':', $data{timestamp}, @{$datum->{data}}) );
112         if ( my $ERROR = RRDs::error ) { print "ERROR: $ERROR\n"; exit $ERRORS{UNKNOWN}; }
113     }
116 # --------------------------------------------------------------------------- #
118 if ( defined($debug_file) ) {
119     print LOGFILE "-------------------------------------------------------------------------------\n";
120     close( LOGFILE );
123 exit $ERRORS{OK};
125 # /opt=value,value,value:/=value,value,value - into multiple rrd's
126 sub process_multi {
127     my( $datum, $create ) = @_;
129     # return a string for creating new RRDs
130     if ( defined($create) && $create == 1 ) {
131         my @DS = ();
132         for ( my $i=0; $i<scalar(@{$datum->{data}}); $i++ ) {
133             # allow the RRD type to be set in the switch/if above
134             my $tmp_rrd_type = $rrd_type;
135             if ( ref($rrd_type) eq 'ARRAY' ) { $tmp_rrd_type = $rrd_type->[$i] } 
136             # put the new datasource into the list
137             push( @DS, "DS:$data{metric}$i:GAUGE:86400:U:U" );
138         }
139         return @DS;
140     }
141     # return a template for updating an RRD
142     elsif ( defined($datum) && !defined($create) ) {
143         my @template = ();
144         for ( my $i=0; $i<scalar(@{$datum->{data}}); $i++ ) {
145             push( @template, $data{metric}.$i );
146         }
147         return join( ':', @template );
148     }
149     # break the data up into parts for processing (updates and creates)
150     else {
151         my @processed = ();
152         foreach my $part ( split(/:/, $data{perfdata}) ) { # break the line into parts
153             my @parts = split( /,/, $part ); # break the part into parts
154             my $rrd_name = $parts[0]; # figure out a good name for the RRD
155             if ( $parts[0] =~ /^\// ) { # handle /'s in disk names
156                 $rrd_name = $parts[0];
157                 $rrd_name =~ s#/#_#g; $rrd_name =~ s/^_//; $rrd_name =~ s/_$//;
158                 if ( $parts[0] eq '/' ) { $rrd_name = 'root' };
159             }
160             # store our munged data in an array of hashes
161             push( @processed, { rrd_name => $rrd_name, name => shift(@parts), data => [ @parts ] } );
162         }
163         return @processed;
164     }
167 # name=value:name=value - into one rrd
168 sub process_single {
169     my( $datum, $create ) = @_;
171     my( @names, @values ) = ();
172     foreach my $part ( split(/:/, $data{perfdata}) ) {
173         my( $name, $value ) = split( /=/, $part );
174         push( @names,  $name  );
175         push( @values, $value );
176     }
178     if ( defined($create) && $create == 1 ) {
179         my @DS = ();
180         for( my $i=0; $i<scalar(@names); $i++ ) {
181             my $tmp_rrd_type = $rrd_type;
182             if ( ref($rrd_type) eq 'ARRAY' ) { $tmp_rrd_type = $rrd_type->[$i] } 
183             push( @DS, 'DS:'.$names[$i].":$tmp_rrd_type:86400:U:U" );
184         }
185         return @DS;
186     }
187     elsif ( defined($datum) && !defined($create) ) {
188         return join( ':', @names );
189     }
190     else {
191         return( {rrd_name=>lc($data{metric}), name=>$data{metric}, data=>[@values]} );
192     }