#!@PERL@ -w # nagios: -epn ## @PKG_NAME@–@PKG_VERSION@ # # Copyright (c) 2006-2015 PNP4Nagios Developer Team (http://www.pnp4nagios.org) # # 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. # # There are several Perl modules out there on www.cpan.org which allow adding # columns to an RRD file, so if this one doesn't fit your needs please have # a look there. # Please report any errors nevertheless. # See http://docs.pnp4nagios.org/pnp-0.6/about#support for details. use strict; use warnings; # you may have to adapt the path to your environment my $rrdtool = "@RRDTOOL@"; # set to 1 if using PNP4Nagios where data source names are numerical my $PNP = 1; ### ---------------------------------------- ### ### no user servicable parts below this line ### my $pgm = "rrd_modify.pl"; my $version = "0.01"; my $legal = "Copyright (c) 2012 PNP4Nagios Developer Team (http://www.pnp4nagios.org)"; my ($rrd,$action,$columns,$type) = @ARGV; # valid actions my %action = (insert => 1, delete => 2); # valid data types my %type = (GAUGE => 1, COUNTER => 2, DERIVE => 3, ABSOLUTE => 4, COMPUTE => 5); # xml tags within cdp_prep my @cdp = ('primary_value','secondary_value','value', 'unknown_datapoints','seasonal','last_seasonal', 'init_flag','intercept','last_intercept', 'slope','last_slope','nan_count','last_nan_count'); my @ds = (); # number of data sources within the rrd file my $out = 1; # output lines to file my $sign = "."; # decimal sign (locale dependent) my %xml = (); # lines within ds structure # defined? if ($#ARGV < 2) { usage(); exit 1; } die "$rrd does not exist\n" unless (-f $rrd); die "$rrd is not readable\n" unless (-r $rrd); die "$rrd is not writable\n" unless (-w $rrd); die "$rrdtool is a directory\n" if (-d $rrdtool); die "$rrdtool does not exist\n" unless (-f $rrdtool); die "$rrdtool is not executable\n" unless (-x $rrdtool); $action = lc($action); unless (exists($action{$action})) { print "ERROR: action $action is not valid\n\n"; usage(); exit 1; } my ($start,$no) = split(/,/,$columns); $no = 1 unless (defined $no); # determine the number of data sources my $ds = `$rrdtool info $rrd | grep '^ds' | grep 'value' | wc -l`; # determine the decimal sign $sign = `$rrdtool info $rrd | grep '^ds' | grep 'value' | tail -1`; ($sign) = $sign =~ /.* \d(.)\d+/; my $end = ($action eq "insert" ? $ds+$no : $ds); if (($start < 1) or ($start > $ds + 1)) { print "ERROR: number ($start) must be within 1..".($ds+1)."\n"; exit 2; } # check / set type of data source to be created if ($action eq "insert") { if (defined $type) { $type = uc($type); unless (exists($type{$type})) { print "ERROR: type $type is not valid\n\n"; usage(); exit 3; } } else { $type = "GAUGE"; } } # names of temporary/output files my $tmp1 = "$rrd.in"; my $tmp2 = "$rrd.out"; my $cmd = "$rrdtool dump $rrd > $tmp1"; my $erg = system("$cmd"); print "$cmd: RC=$erg\n" if ($erg); processing ("$rrd"); $cmd = "$rrdtool restore $tmp2 $rrd.chg"; $erg = system("$cmd"); print "$cmd: RC=$erg\n" if ($erg); unlink "$tmp1"; unlink "$tmp2"; exit; ### some sub routines sub processing { open (IFILE, "$tmp1") || die "error during open of $tmp1, RC=$!\n"; open (OFILE, ">$tmp2") || die "error during create of $tmp2, RC=$!\n"; while () { my $tmp = $_; if (//) { $out = 0; %xml = (); next; } if (/<\/ds>/) { my %tmp = %xml; push @ds, \%tmp; next; } if ((m|Round Robin Archives|) or (m||)) { $out = 1; if ($action eq "insert") { my @save = splice(@ds,$start-1); for (1..$no) { my %xml = (@save) ? %{$save[0]} : %{$ds[0]}; $xml{name} = $start+$_-1; # set defaults if (m|Round Robin Archives|) { $xml{last_ds} = "U"; $xml{value} = "0${sign}0000000000e+00"; $xml{unknown_sec} = 0; } else { $xml{primary_value} = "0${sign}0000000000e+00"; $xml{secondary_value} = "0${sign}0000000000e+00"; $xml{value} = "NaN" if (exists $xml{value}); $xml{unknown_datapoints} = 0 if (exists $xml{unknown_datapoints}); $xml{init_flag} = 1 if (exists $xml{init_flag}); $xml{seasonal} = "NaN" if (exists $xml{seasonal}); $xml{last_seasonal} = "NaN" if (exists $xml{last_seasonal}); $xml{intercept} = "NaN" if (exists $xml{intercept}); $xml{last_intercept} = "NaN" if (exists $xml{last_intercept}); $xml{slope} = "NaN" if (exists $xml{slope}); $xml{last_slope} = "NaN" if (exists $xml{last_slope}); $xml{nan_count} = 1 if (exists $xml{nan_count}); $xml{last_nan_count} = 1 if (exists $xml{last_nan_count}); } push @ds,\%xml; } push @ds,@save; } else { my @save = splice(@ds,$start-1,$no); if ($PNP) { # renumber data source names for my $idx ($start..$end-$no) { $ds[$idx-1]->{name} = $idx; } } } if (m|Round Robin Archives|) { out_ds1(); } else { out_ds2(); } print OFILE $tmp; @ds = (); next; } if (//) { row($_); next; } # value enclosed in XML tags if (/<(\S+)>\s+(\S+)\s+)(.*)<\/row>/; for (1..$start-1) { if ($r =~ s#^(.*?)##) { $line .= $1; } } for ($start..$start+$no-1) { if ($action eq "insert") { $line .= " NaN "; } else { $r =~ s#^(.*?)##; } } for ($start+$no..$end) { if ($r =~ s#^(.*?)##) { $line .= $1; } } $line .= "\n"; print OFILE $line; } sub out_ds1 { for (0..$#ds) { print OFILE < \t\t $ds[$_]->{name} \t\t $ds[$_]->{type} \t\t $ds[$_]->{minimal_heartbeat} \t\t $ds[$_]->{min} \t\t $ds[$_]->{max} \t\t \t\t $ds[$_]->{last_ds} \t\t $ds[$_]->{value} \t\t $ds[$_]->{unknown_sec} \t EOD } } sub out_ds2 { for my $ds_no (0..$#ds) { print OFILE "\t\t\t\n"; for my $tag (0..$#cdp) { print OFILE "\t\t\t<$cdp[$tag]> $ds[$ds_no]->{$cdp[$tag]} \n" if (exists($ds[$ds_no]->{$cdp[$tag]})); } print OFILE "\t\t\t\n"; } } sub usage { print <