#!/usr/local/bin/perl -w # -*- perl -*- # Cricket: a configuration, polling and data display wrapper for RRD files # # Copyright (C) 1998 Jeff R. Allen and WebTV Networks, Inc. # # 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., 675 Mass Ave, Cambridge, MA 02139, USA. BEGIN { my $programdir = (($0 =~ m:^(.*/):)[0] || "./") . ".."; eval "require '$programdir/cricket-conf.pl'"; eval "require '/usr/local/etc/cricket-conf.pl'" unless $Common::global::gInstallRoot; $Common::global::gInstallRoot ||= $programdir; } use lib "$Common::global::gInstallRoot/lib"; use RRDs 1.000101; use ConfigTree::Cache; use Common::HandleTarget; use Common::Map; use Common::Log; use Getopt::Long; my($VERSION) = "rrd-tune 1.1"; # Parse common options & script specific arguments my($clear) = localOptions(); $Common::global::gCT = new ConfigTree::Cache; $gCT = $Common::global::gCT; $gCT->Base($Common::global::gConfigRoot); $gCT->Warn(\&Warn); if (! $gCT->init()) { Die("Failed to open compiled config tree from " . "$Common::global::gConfigRoot/config.db: $!"); } my($recomp, $why) = $gCT->needsRecompile(); if ($recomp) { Die("Config tree needs to be recompiled: $why"); } # Remaining arguments are the subtrees my (@root) = (@ARGV) ? @ARGV : "/"; # foreach subtree to do # find the base node of that subtree # foreach leaf node of this subtree # process it my($subtree); foreach $subtree (@root) { if ($gCT->nodeExists($subtree)) { $gCT->visitLeafs($subtree, \&handleTarget, \&handleTargetInstance, \&localHandleTargetInstance); } else { Warn("Unknown subtree $subtree."); } } exit; sub localHandleTargetInstance { my($name, $target) = @_; my($tname) = $target->{'auto-target-name'}; # don't try to tune multitargets if ($target->{'targets'}) { return; } my($ttype) = $target->{'target-type'}; if (! $ttype) { Warn("Skipping RRD tune for $tname; no target type."); return; } my($ttRef) = $main::gCT->configHash($name, 'targettype', lc($ttype), $target); if (! defined($ttRef)) { Warn("Skipping RRD tune for $tname; unknown target type."); return; } my($dslist) = $ttRef->{'ds'}; if (! defined($dslist)) { Warn("Skipping RRD tune for $tname; no DS's for target-type $ttype."); return; } # Does the RRD file exist my($rrd) = $target->{'rrd-datafile'}; if (! $rrd) { Warn("Skipping RRD tune for $tname; could not find " . " rrd-datafile."); return; } # Run the tuning agent to ajust RRD max and min values for each DS of the RRD tuneRRD($name, $target, $ttype, $ttRef, $dslist, $rrd); # Remove orphaned alarms in the meta data file associated with a valid RRD cleanMeta($name, $target); return 1; } sub cleanMeta { my($name, $target) = @_; my($tname) = $target->{'auto-target-name'}; return if ($tname ne "chassis"); ### meta file processing that contains active alarms my($rrd) = new RRD::File( -file=>$target->{'rrd-datafile'} ); my($refMeta) = $rrd->getMeta(); ### create a space delimited string with the monitor thresholds my ($monitors); if (defined $target->{'monitor-thresholds'}) { $monitors = ' ' . $target->{'monitor-thresholds'}; $monitors =~ s/[\s,]+/ /g; } foreach my $key (keys %{$refMeta}) { Info (" Clear this alarm?: $key data: $refMeta->{$key}"); next if ($key =~ /last-inst/); if ($monitors) { my $found = ($monitors =~ / $key /) ? 1 : 0; delete($refMeta->{$key}) if (!$found || $clear); Info (" Clearing alarm: $key") if (!$found || $clear); } else { delete($refMeta->{$key}); Info (" Clearing alarm: $key"); } } $rrd->setMeta($refMeta); return 1; } sub tuneRRD { my($name, $target, $ttype, $ttRef, $dslist, $rrd) = @_; my($tname) = $target->{'auto-target-name'}; # run rrd tune on the RRD my(@arg); my($dsnum) = 0; my($dsname); foreach $dsname (split(/\s*,\s*/, $dslist)) { my($ds) = $main::gCT->configHash($name, 'datasource', lc($dsname), $target); if (! defined($ds)) { Warn("Unknown datasource: $dsname"); next; } my($dst) = $ds->{'rrd-ds-type'}; $dst = "GAUGE" unless (defined($dst)); $dst = uc($dst); # Never tune COMPUTE data sources; it will corrupt the header. # If the user really wants to replace the COMPUTE data source with # another type, they can do it manually. if ($dst eq 'COMPUTE') { $dsnum++; Info("Skipping RRD tune for COMPUTE data source $dsname"); next; } my($hb) = $ds->{'rrd-heartbeat'}; $hb = $target->{'rrd-heartbeat'} if (defined($target->{'rrd-heartbeat'})); $hb = 1800 unless (defined($hb)); my($min) = $ds->{'rrd-min'}; $min = $target->{'rrd-min'} if (defined($target->{'rrd-min'})); $min = 'U' unless (defined($min)); my($max) = $ds->{'rrd-max'}; $max = $target->{'rrd-max'} if (defined($target->{'rrd-max'})); $max = 'U' unless (defined($max)); push(@arg, '-d', "ds$dsnum:$dst", '-h', "ds$dsnum:$hb", '-i', "ds$dsnum:$min", '-a', "ds$dsnum:$max"); $dsnum++; } Info("Tuning $tname"); Debug("RRDs::tune $rrd ", join(" ", @arg)); RRDs::tune $rrd, @arg; my($err) = RRDs::error(); if ($error) { Warn("Unable to tune $rrd: $error\n"); return; } return 1; } sub localOptions { my ($logLevel, $logFormat, $base, $clear, $usage); # default to 'info' unless there's a environment variable # or a commandline arg $logLevel = $Common::global::gLogLevel if $Common::global::gLogLevel; $logLevel = $ENV{'CRICKET_LOG_LEVEL'} if $ENV{'CRICKET_LOG_LEVEL'}; $logLevel ||= "info"; # default to 'standard' unless there's a environment variable $logFormat = $Common::global::gLogFormat if $Common::global::gLogFormat; $logFormat = $ENV{'CRICKET_LOG_FORMAT'} if $ENV{'CRICKET_LOG_FORMAT'}; $logFormat ||= "standard"; GetOptions( "loglevel:s" => \$logLevel, "logformat:s" => \$logFormat, "base:s" => \$base, "clear" => \$clear, "help|h" => \$usage); usage() if ($usage); $Common::global::gConfigRoot = $base if $base; Common::Log::setLevel($logLevel); Common::Log::setFormat($logFormat); return ($clear); } sub usage { #' print STDERR < Default Actions: - Cleanout orphaned monitor thresholds alarms - Ajust min and max RRD database values --clear - Remove all monitor threshold alarms - Select a protion of the config-tree --loglevel - Select the logging level --logformat - Select the logging format --help | -h - Display the current help file EXAMPLE: $0 EXAMPLE: $0 /cisco-routers/siteA/routerA EXAMPLE: $0 --clear /cisco-routers EXAMPLE: $0 --clear EOD #' exit(1); }