#!/usr/bin/perl # v1.0 # nagiostat, program to insert performance-data from Nagios into RRD-archives # Copyright (C) 2004 Carl Bingel / Svensk IT konsult AB # # This program 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; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use strict; ## Basic configuration options my $BASE_DIR = "/usr/share/nagiostat"; my $CONFIG_FILE = "/etc/nagios/nagiostat.conf"; ## Config-file location my $DEBUG_LOG_FILE = "/var/spool/nagiostat/debug.log"; ## Specify where to create log-file and what filename (must be writable by nagios-user!) my $DEBUGLEVEL = 1; ## 0=Nothing, 1=Errors, 2=Warnings, 3=Debug my $DEBUGOUTPUT = 0; ## 0=file, 1=STDERR, 2=STDOUT (for cgi) require 'shellwords.pl'; ## Global vars my $DEBUG_TIMESTAMP=0; ## Find out how program is run if( $ARGV[0] eq "-t") { ## -t = test configuration-file print STDERR "nagiostat: Testing configuration-file..\n"; $DEBUGLEVEL=3; $DEBUGOUTPUT=1; ## output errors to console and not file my $c = &read_config(); abort(); } elsif( $ARGV[0] eq "-p") { ## -p = parse performance-data (when started by nagios) &parse_perfdata(); } else { if( exists $ENV{'GATEWAY_INTERFACE'}) { ## we are run as a CGI-script! $DEBUGOUTPUT=2; ## output errors to web-browser &run_as_cgi(); } else { ## print some help-info print STDERR "nagiostat: usage: -t Test configuration-file -p Parse/import performance-data (used when called from nagios) "; } } abort(); sub abort { ## logfile: write blank if we wrote anything... if( $DEBUG_TIMESTAMP!=0) { debug( 1, ""); } exit; } ## ## Program is called as CGI ## sub run_as_cgi { use CGI; my $cgi = new CGI; my $graph_name = $cgi->param( "graph_name"); my $graph_iteration = $cgi->param( "graph_iteration"); if( $graph_iteration eq "") { print "Content-type: text/html\nExpires: 0\n\n"; } else { print "Content-type: image/gif\nExpires: 0\n\n"; } my $config = read_config(); if( $graph_name eq "") { ## ## display index of graphs ## display_htmltemplate( $config->{'htmltemplatepath'}."/".$config->{'graphindextemplate'}, $graph_name, $config); } else { ## display graph if( ! exists $config->{'graphs'}->{$graph_name}) { debug( 1, "ERROR: Graph '$graph_name' does not exist!"); exit; } elsif( $graph_iteration eq "") { ## ## Display HTML-page with all the graphs ## if( ! -r $config->{'htmltemplatepath'}."/".$config->{'graphs'}->{$graph_name}->{'htmltemplate'}) { debug( 1, "ERROR: HTML-template '".($config->{'htmltemplatepath'}."/".$config->{'graphs'}->{$graph_name}->{'htmltemplate'})."' is not readable by effective userid!"); exit; } display_htmltemplate( $config->{'htmltemplatepath'}."/".$config->{'graphs'}->{$graph_name}->{'htmltemplate'}, $graph_name, $config); } else { ## ## generate graph (call 'rrdtool graph') ## my $rrdtool_cmdline = $config->{'rrdtoolpath'}." graph - ".join( " ", @{$config->{'plottemplates'}->{ $config->{'graphs'}->{$graph_name}->{'plottemplate'} } }); ## expand variables my $rrdarchive = $config->{'rrdarchivepath'}."/".$config->{'graphs'}->{$graph_name}->{'rrdfilename'}; $rrdtool_cmdline =~ s/\$f/$rrdarchive/g; my $t_start = $config->{'graphtimetemplates'}->{ $config->{'graphs'}->{$graph_name}->{'graphtimetemplate'} }->[$graph_iteration]->{'starttime'}; $rrdtool_cmdline =~ s/\$s/$t_start/g; my $t_end = $config->{'graphtimetemplates'}->{ $config->{'graphs'}->{$graph_name}->{'graphtimetemplate'} }->[$graph_iteration]->{'endtime'}; $rrdtool_cmdline =~ s/\$e/$t_end/g; my $t_descr = $config->{'graphtimetemplates'}->{ $config->{'graphs'}->{$graph_name}->{'graphtimetemplate'} }->[$graph_iteration]->{'description'}; $rrdtool_cmdline =~ s/\$d/$t_descr/g; ## Call rrdtool (should probably be fixed to call it in a better way, like exec) print `$rrdtool_cmdline`; } } } ## Display HTML template (and do variable-substitution and other stuff) ## sub display_htmltemplate { my( $filename, $graph_name, $config) = @_; if( -r $filename) { open( HTML, $filename); while( ) { ## All is a big regex.. :-) s/\$(\w+)/my $t=sub { my $varname = $_[0]; if( $varname eq "GRAPHNAME") { ## return the name of the graph if( $config->{'graphs'}->{$graph_name}->{'title'} ne "") { return( $config->{'graphs'}->{$graph_name}->{'title'}); } else { return( "Graph ".$graph_name); } } elsif( $varname eq "CURRENTTIME") { ## return current date-time return( localtime()); } elsif( $varname eq "GRAPHINDEX" || $varname eq "GRAPHINDEX_ONEROW") { ## return HTML-code for index of the different graphs my $return_html; foreach my $gn ( sort keys %{$config->{'graphs'}}) { $return_html.=(($varname eq "GRAPHINDEX")?"
".($time->{'description'}).""; 
                 $iteration_id++;
               }
               return( $return_html);
            } else {                                                 ## unknown variable
               return( "##UNKNOWN-VARIABLE##");
            }
         }; &$t($1)/eig;   ## i thought that regex would never end!
         print;
      }
      close( HTML);
   } else {
      print "ERROR: HTML-template '$filename' does not exist or is not readable by effective userid.";
   }
}
##
##  Process incoming performance-data (parse output from check-plugin and insert values into rrd-archives)
##
sub parse_perfdata {
  $DEBUG_TIMESTAMP=0;
  
  my $config = read_config();
  my $rrd_updates;
  ##  Provide more symbolic names (same names as the macros in nagios configuration-file)
  my( $LASTCHECK, $HOSTNAME, $SERVICEDESCR, $SERVICESTATE, $OUTPUT, $PERFDATA) = split( /\|!!\|/, $ARGV[1]);
  debug( 3, "**INCOMING PERFDATA:\n  LASTCHECK=$LASTCHECK\n  HOSTNAME=$HOSTNAME\n  SERVICEDESCR=\"$SERVICEDESCR\"\n  SERVICESTATE=\"$SERVICESTATE\"\n  OUTPUT=\"$OUTPUT\"\n  PERFDATA=\"$PERFDATA\"");
  
  my $host_and_descr_found;
  ## Loop through all host_regexes
  foreach my $host_regex ( keys %{$config->{'regexes'}}) {
    ## Loop through all service_description_regexes
    foreach my $service_regex ( keys %{$config->{'regexes'}->{$host_regex}}) {
      if( ($HOSTNAME =~ m/$host_regex/i) && ($SERVICEDESCR =~ m/$service_regex/i) ) {     ## match!
        $host_and_descr_found=1;
        ## Loop through all InsertValue-lines with same host and service_description match
        foreach my $insert_value ( @{$config->{'regexes'}->{$host_regex}->{$service_regex}} ) {
          ## Loop through all regexes that should match values in the output/perfdata
          foreach my $regex ( @{ $config->{'valueregextemplates'}->{$insert_value->{'regextemplate'}} }) {
             my $regex_string = $regex->{'regex'};
             if( $regex->{'regex_what'} eq "output") {         ## do regex on "output"
                if( $OUTPUT =~ m/$regex_string/) {
                   debug( 3, " +VALUE: ".$1);
                   push( @{$rrd_updates->{$insert_value->{'rrdarchive'}}->{'value'}}, $1);
                   push( @{$rrd_updates->{$insert_value->{'rrdarchive'}}->{'dsaname'}}, $regex->{'dsaname'});
                   $rrd_updates->{$insert_value->{'rrdarchive'}}->{'rrdcreatetemplate'} = $insert_value->{'rrdcreatetemplate'};  #$config->{'regexes'}->{$host_regex}->{$service_regex}->[0]->{'rrdcreatetemplate'};
                } else {
                   debug( 2, "**WARNING: No match for value with regex on output '$regex_string'.");
                }
             } else {                                          ## do regex on "perfdata"
                if( $PERFDATA =~ m/$regex_string/) {
                   debug( 3, " +VALUE: ".$1);
                   push( @{$rrd_updates->{$insert_value->{'rrdarchive'}}->{'value'}}, $1);
                   push( @{$rrd_updates->{$insert_value->{'rrdarchive'}}->{'dsaname'}}, $regex->{'dsaname'});
                   $rrd_updates->{$insert_value->{'rrdarchive'}}->{'rrdcreatetemplate'} = $insert_value->{'rrdcreatetemplate'};  #$config->{'regexes'}->{$host_regex}->{$service_regex}->[0]->{'rrdcreatetemplate'};
                } else {
                   debug( 2, "**WARNING: No match for value with regex on perfdata '$regex_string'.");
                }
             }
          }
        }
        
      }
    }
  }
  
  if( !$host_and_descr_found) {
    debug( 2, "**WARNING: Hostname and description didn't match any of the regexes in the config-file.");
  } else {
    ##
    ## Insert the value into the RRD by calling the rrdtool (may be several rrd-archives)
    ##
    foreach my $archive ( keys %{$rrd_updates}) {
      debug( 3, " =INSERT into '$archive': ".join( ",", @{$rrd_updates->{$archive}->{'value'}} )." DSA-names=".join( ",", @{$rrd_updates->{$archive}->{'dsaname'}}) );
      
      my $rrdarchive_filename = $config->{'rrdarchivepath'}."/".$archive;
      
      ## Create RRD-Archive (according to template) if it does not exist
      if( ! -e $rrdarchive_filename) {
         my $rrdtool_cmdline = $config->{'rrdtoolpath'}." create ".$rrdarchive_filename." ".(join( " ", @{$config->{'rrdcreatetemplates'}->{$rrd_updates->{$archive}->{'rrdcreatetemplate'}}}));
         debug( 2, "**CREATING: $rrdarchive_filename, cmdline='".$rrdtool_cmdline."'.");
         `$rrdtool_cmdline`;
      }
      ## Check if rrd-archive is writable
      if( ! -w $rrdarchive_filename) {					## check wheter RRD-archive exists
        debug( 1, "!!ERROR: RRD-archive '".$rrdarchive_filename."' does not exist or is not writable by effective UID."); abort();
      }
      ## Assemle command-line for rrdtool
      my $rrdtool_cmdline = $config->{'rrdtoolpath'}." update ".$rrdarchive_filename.
	                    " --template ".join( ":", @{$rrd_updates->{$archive}->{'dsaname'}}).
                            " $LASTCHECK:".join( ":", @{$rrd_updates->{$archive}->{'value'}});
      debug( 3, " !RRDCMDLINE: ".$rrdtool_cmdline);
      my $result = `$rrdtool_cmdline`;
      if( $result ne "") {
        debug( 1, "!!RESULT: $result");
      }
    }
  }
}
##
##  Read config-file and check for errors
##
sub read_config {
  my $config;
  open( CONFIG, $CONFIG_FILE);
  my( $line_counter);
  while( 
$msg
";
    } else {
      print STDERR $msg."\n";
    }
  }
}