diff --git a/scripts/removespikes.php b/scripts/removespikes.php
new file mode 100755
index 000000000..1d4af1da8
--- /dev/null
+++ b/scripts/removespikes.php
@@ -0,0 +1,838 @@
+This script is only meant to run at the command line.");
+}
+
+/* We are not talking to the browser */
+$no_http_headers = true;
+
+$dir = dirname(__FILE__);
+chdir($dir);
+
+if (strpos($dir, 'spikekill') !== false) {
+ chdir('../../');
+}
+
+$using_cacti = false;
+
+/* setup defaults */
+$debug = FALSE;
+$dryrun = FALSE;
+$avgnan = 'avg';
+$rrdfile = "";
+$std_kills = TRUE;
+$var_kills = TRUE;
+$html = FALSE;
+
+if ($using_cacti) {
+ $method = read_config_option("spikekill_method");
+ $numspike = read_config_option("spikekill_number");
+ $stddev = read_config_option("spikekill_deviations");
+ $percent = read_config_option("spikekill_percent");
+ $outliers = read_config_option("spikekill_outliers");
+}else{
+ $method = 1; // Standard Deviation
+ $numspike = 10;
+ $stddev = 10;
+ $percent = 500;
+ $outliers = 5;
+}
+
+/* process calling arguments */
+$parms = $_SERVER["argv"];
+array_shift($parms);
+
+foreach($parms as $parameter) {
+ @list($arg, $value) = @explode("=", $parameter);
+
+ switch ($arg) {
+ case "--method":
+ case "-M":
+ if ($value == "variance") {
+ $method = 2;
+ }elseif ($value == "stddev") {
+ $method = 1;
+ }else{
+ echo "FATAL: You must specify either 'stddev' or 'variance' as methods.\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "--avgnan":
+ case "-A":
+ if ($value == "avg") {
+ $avgnan = "avg";
+ }elseif ($value == "nan") {
+ $avgnan = "nan";
+ }else{
+ echo "FATAL: You must specify either 'avg' or 'nan' as replacement methods.\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "--rrdfile":
+ case "-R":
+ $rrdfile = $value;
+
+ if (!file_exists($rrdfile)) {
+ echo "FATAL: File '$rrdfile' does not exist.\n";
+ exit;
+ }
+
+ if (!is_writable($rrdfile)) {
+ echo "FATAL: File '$rrdfile' is not writable by this account.\n";
+ exit;
+ }
+
+ break;
+ case "--stddev":
+ case "-S":
+ $stddev = $value;
+
+ if (!is_numeric($stddev) || ($stddev < 1)) {
+ echo "FATAL: Standard Deviation must be a positive integer.\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "--outliers":
+ case "-O":
+ $outliers = $value;
+
+ if (!is_numeric($outliers) || ($outliers < 1)) {
+ echo "FATAL: The number of outliers to exlude must be a positive integer.\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "--percent":
+ case "-P":
+ $percent = $value/100;
+
+ if (!is_numeric($percent) || ($percent <= 0)) {
+ echo "FATAL: Percent deviation must be a positive floating point number.\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "--html":
+ $html = TRUE;
+
+ break;
+ case "-d":
+ case "--debug":
+ $debug = TRUE;
+
+ break;
+ case "-D":
+ case "--dryrun":
+ $dryrun = TRUE;
+
+ break;
+ case "--number":
+ case "-n":
+ $numspike = $value;
+
+ if (!is_numeric($numspike) || ($numspike < 1)) {
+ echo "FATAL: Number of spikes to remove must be a positive integer\n\n";
+ display_help();
+ exit;
+ }
+
+ break;
+ case "-h":
+ case "-v":
+ case "-V":
+ case "--version":
+ case "--help":
+ display_help();
+ exit;
+ default:
+ print "ERROR: Invalid Parameter " . $parameter . "\n\n";
+ display_help();
+ exit;
+ }
+}
+
+/* additional error check */
+if ($rrdfile == "") {
+ echo "FATAL: You must specify an RRDfile!\n\n";
+ display_help();
+ exit;
+}
+
+/* determine the temporary file name */
+$seed = mt_rand();
+if ($config["cacti_server_os"] == "win32") {
+ $tempdir = getenv("TEMP");
+ $xmlfile = $tempdir . "/" . str_replace(".rrd", "", basename($rrdfile)) . ".dump." . $seed;
+}else{
+ $tempdir = "/tmp";
+ $xmlfile = "/tmp/" . str_replace(".rrd", "", basename($rrdfile)) . ".dump." . $seed;
+}
+
+if ($html) {
+ echo "
";
+}
+
+if ($using_cacti) {
+ cacti_log("NOTE: Removing Spikes for '$rrdfile', Method:'$method'", false, "WEBUI");
+}
+
+/* execute the dump command */
+echo ($html ? "| ":"") . "NOTE: Creating XML file '$xmlfile' from '$rrdfile'" . ($html ? " |
\n":"\n");
+
+if ($using_cacti) {
+ shell_exec(read_config_option("path_rrdtool") . " dump $rrdfile > $xmlfile");
+}else{
+ shell_exec("rrdtool dump $rrdfile > $xmlfile");
+}
+
+/* read the xml file into an array*/
+if (file_exists($xmlfile)) {
+ $output = file($xmlfile);
+
+ /* remove the temp file */
+ unlink($xmlfile);
+}else{
+ if ($using_cacti) {
+ echo ($html ? "| ":"") . "FATAL: RRDtool Command Failed. Please verify that the RRDtool path is valid in Settings->Paths!" . ($html ? " |
\n":"\n");
+ }else{
+ echo ($html ? "| ":"") . "FATAL: RRDtool Command Failed. Please insure RRDtool is in your path!" . ($html ? " |
\n":"\n");
+ }
+ exit;
+}
+
+/* process the xml file and remove all comments */
+$output = removeComments($output);
+
+/* Read all the rra's ds values and obtain the following pieces of information from each
+ rra archive.
+
+ * numsamples - The number of 'valid' non-nan samples
+ * sumofsamples - The sum of all 'valid' samples.
+ * average - The average of all samples
+ * standard_deviation - The standard deviation of all samples
+ * max_value - The maximum value of all samples
+ * min_value - The minimum value of all samples
+ * max_cutoff - Any value above this value will be set to the average.
+ * min_cutoff - Any value lower than this value will be set to the average.
+
+ This will end up being a n-dimensional array as follows:
+ rra[x][ds#]['totalsamples'];
+ rra[x][ds#]['numsamples'];
+ rra[x][ds#]['sumofsamples'];
+ rra[x][ds#]['average'];
+ rra[x][ds#]['stddev'];
+ rra[x][ds#]['max_value'];
+ rra[x][ds#]['min_value'];
+ rra[x][ds#]['max_cutoff'];
+ rra[x][ds#]['min_cutoff'];
+
+ There will also be a secondary array created with the actual samples. This
+ array will be used to calculate the standard deviation of the sample set.
+ samples[rra_num][ds_num][];
+
+ Also track the min and max value for each ds and store it into the two
+ arrays: ds_min[ds#], ds_max[ds#].
+
+ The we don't need to know the type of rra, only it's number for this analysis
+ the same applies for the ds' as well.
+*/
+$rra = array();
+$rra_cf = array();
+$rra_pdp = array();
+$rra_num = 0;
+$ds_num = 0;
+$total_kills = 0;
+$in_rra = false;
+$in_db = false;
+$ds_min = array();
+$ds_max = array();
+$ds_name = array();
+
+/* perform a first pass on the array and do the following:
+ 1) Get the number of good samples per ds
+ 2) Get the sum of the samples per ds
+ 3) Get the max and min values for all samples
+ 4) Build both the rra and sample arrays
+ 5) Get each ds' min and max values
+*/
+if (sizeof($output)) {
+foreach($output as $line) {
+ if (substr_count($line, "")) {
+ $linearray = explode("", $line);
+ /* discard the row */
+ array_shift($linearray);
+ $ds_num = 0;
+ foreach($linearray as $dsvalue) {
+ /* peel off garbage */
+ $dsvalue = trim(str_replace("", "", str_replace("", "", $dsvalue)));
+ if (strtolower($dsvalue) != "nan") {
+ if (!isset($rra[$rra_num][$ds_num]["numsamples"])) {
+ $rra[$rra_num][$ds_num]["numsamples"] = 1;
+ }else{
+ $rra[$rra_num][$ds_num]["numsamples"]++;
+ }
+
+ if (!isset($rra[$rra_num][$ds_num]["sumofsamples"])) {
+ $rra[$rra_num][$ds_num]["sumofsamples"] = $dsvalue;
+ }else{
+ $rra[$rra_num][$ds_num]["sumofsamples"] += $dsvalue;
+ }
+
+ if (!isset($rra[$rra_num][$ds_num]["max_value"])) {
+ $rra[$rra_num][$ds_num]["max_value"] = $dsvalue;
+ }else if ($dsvalue > $rra[$rra_num][$ds_num]["max_value"]) {
+ $rra[$rra_num][$ds_num]["max_value"] = $dsvalue;
+ }
+
+ if (!isset($rra[$rra_num][$ds_num]["min_value"])) {
+ $rra[$rra_num][$ds_num]["min_value"] = $dsvalue;
+ }else if ($dsvalue < $rra[$rra_num][$ds_num]["min_value"]) {
+ $rra[$rra_num][$ds_num]["min_value"] = $dsvalue;
+ }
+
+ /* store the sample for standard deviation calculation */
+ $samples[$rra_num][$ds_num][] = $dsvalue;
+ }
+
+ if (!isset($rra[$rra_num][$ds_num]["totalsamples"])) {
+ $rra[$rra_num][$ds_num]["totalsamples"] = 1;
+ }else{
+ $rra[$rra_num][$ds_num]["totalsamples"]++;
+ }
+
+ $ds_num++;
+ }
+ } elseif (substr_count($line, "")) {
+ $in_rra = true;
+ } elseif (substr_count($line, "")) {
+ $ds_min[] = trim(str_replace("", "", str_replace("", "", trim($line))));
+ } elseif (substr_count($line, "")) {
+ $ds_max[] = trim(str_replace("", "", str_replace("", "", trim($line))));
+ } elseif (substr_count($line, "")) {
+ $ds_name[] = trim(str_replace("", "", str_replace("", "", trim($line))));
+ } elseif (substr_count($line, "")) {
+ $rra_cf[] = trim(str_replace("", "", str_replace("", "", trim($line))));
+ } elseif (substr_count($line, "")) {
+ $rra_pdp[] = trim(str_replace("", "", str_replace("", "", trim($line))));
+ } elseif (substr_count($line, "")) {
+ $in_rra = false;
+ $rra_num++;
+ } elseif (substr_count($line, "")) {
+ $step = trim(str_replace("", "", str_replace("", "", trim($line))));
+ }
+}
+}
+
+/* For all the samples determine the average with the outliers removed */
+calculateVarianceAverages($rra, $samples);
+
+/* Now scan the rra array and the samples array and calculate the following
+ 1) The standard deviation of all samples
+ 2) The average of all samples per ds
+ 3) The max and min cutoffs of all samples
+ 4) The number of kills in each ds based upon the thresholds
+*/
+echo ($html ? "| ":"") . "NOTE: Searching for Spikes in XML file '$xmlfile'" . ($html ? " |
\n":"\n");
+calculateOverallStatistics($rra, $samples);
+
+/* debugging and/or status report */
+if ($debug || $dryrun) {
+ outputStatistics($rra);
+}
+
+/* create an output array */
+if ($method == 1) {
+ /* standard deviation subroutine */
+ if ($std_kills) {
+ if (!$dryrun) {
+ $new_output = updateXML($output, $rra);
+ }
+ }else{
+ echo ($html ? "| ":"") . "NOTE: NO Standard Deviation Spikes found in '$rrdfile'" . ($html ? " |
\n":"\n");
+ }
+}else{
+ /* variance subroutine */
+ if ($var_kills) {
+ if (!$dryrun) {
+ $new_output = updateXML($output, $rra);
+ }
+ }else{
+ echo ($html ? "| ":"") . "NOTE: NO Variance Spikes found in '$rrdfile'" . ($html ? " |
\n":"\n");
+ }
+}
+
+/* finally update the file XML file and Reprocess the RRDfile */
+if (!$dryrun) {
+ if ($total_kills) {
+ if (writeXMLFile($new_output, $xmlfile)) {
+ if (backupRRDFile($rrdfile)) {
+ createRRDFileFromXML($xmlfile, $rrdfile);
+ }else{
+ echo ($html ? "| ":"") . "FATAL: Unable to backup '$rrdfile'" . ($html ? " |
\n":"\n");
+ }
+ }else{
+ echo ($html ? "| ":"") . "FATAL: Unable to write XML file '$xmlfile'" . ($html ? " |
\n":"\n");
+ }
+ }
+}else{
+ echo ($html ? "| ":"") . "NOTE: Dryrun requested. No updates performed" . ($html ? " |
\n":"\n");
+}
+
+if ($html) {
+ echo "
";
+}
+
+/* All Functions */
+function createRRDFileFromXML($xmlfile, $rrdfile) {
+ global $using_cacti, $html;
+
+ /* execute the dump command */
+ echo ($html ? "| ":"") . "NOTE: Re-Importing '$xmlfile' to '$rrdfile'" . ($html ? " |
\n":"\n");
+ if ($using_cacti) {
+ $response = shell_exec(read_config_option("path_rrdtool") . " restore -f -r $xmlfile $rrdfile");
+ }else{
+ $response = shell_exec("rrdtool restore -f -r $xmlfile $rrdfile");
+ }
+ if (strlen($response)) echo ($html ? "| ":"") . $response . ($html ? " |
\n":"\n");
+}
+
+function writeXMLFile($output, $xmlfile) {
+ return file_put_contents($xmlfile, $output);
+}
+
+function backupRRDFile($rrdfile) {
+ global $using_cacti, $tempdir, $seed, $html;
+
+ if ($using_cacti) {
+ $backupdir = read_config_option("spikekill_backupdir");
+
+ if ($backupdir == "") {
+ $backupdir = $tempdir;
+ }
+ }else{
+ $backupdir = $tempdir;
+ }
+
+ if (file_exists($backupdir . "/" . basename($rrdfile))) {
+ $newfile = basename($rrdfile) . "." . $seed;
+ }else{
+ $newfile = basename($rrdfile);
+ }
+
+ echo ($html ? "| ":"") . "NOTE: Backing Up '$rrdfile' to '" . $backupdir . "/" . $newfile . "'" . ($html ? " |
\n":"\n");
+
+ return copy($rrdfile, $backupdir . "/" . $newfile);
+}
+
+function calculateVarianceAverages(&$rra, &$samples) {
+ global $outliers;
+
+ if (sizeof($samples)) {
+ foreach($samples as $rra_num => $dses) {
+ if (sizeof($dses)) {
+ foreach($dses as $ds_num => $ds) {
+ if (sizeof($ds) < $outliers * 3) {
+ $rra[$rra_num][$ds_num]["variance_avg"] = "NAN";
+ }else{
+ rsort($ds, SORT_NUMERIC);
+ $ds = array_slice($ds, $outliers);
+
+ sort($ds, SORT_NUMERIC);
+ $ds = array_slice($ds, $outliers);
+
+ $rra[$rra_num][$ds_num]["variance_avg"] = array_sum($ds) / sizeof($ds);
+ }
+ }
+ }
+ }
+ }
+}
+
+function calculateOverallStatistics(&$rra, &$samples) {
+ global $percent, $stddev, $ds_min, $ds_max, $var_kills, $std_kills;
+
+ $rra_num = 0;
+ if (sizeof($rra)) {
+ foreach($rra as $dses) {
+ $ds_num = 0;
+
+ if (sizeof($dses)) {
+ foreach($dses as $ds) {
+ if (isset($samples[$rra_num][$ds_num])) {
+ $rra[$rra_num][$ds_num]["standard_deviation"] = standard_deviation($samples[$rra_num][$ds_num]);
+ if ($rra[$rra_num][$ds_num]["standard_deviation"] == "NAN") {
+ $rra[$rra_num][$ds_num]["standard_deviation"] = 0;
+ }
+ $rra[$rra_num][$ds_num]["average"] = $rra[$rra_num][$ds_num]["sumofsamples"] / $rra[$rra_num][$ds_num]["numsamples"];
+
+ $rra[$rra_num][$ds_num]["min_cutoff"] = $rra[$rra_num][$ds_num]["average"] - ($stddev * $rra[$rra_num][$ds_num]["standard_deviation"]);
+ if ($rra[$rra_num][$ds_num]["min_cutoff"] < $ds_min[$ds_num]) {
+ $rra[$rra_num][$ds_num]["min_cutoff"] = $ds_min[$ds_num];
+ }
+
+ $rra[$rra_num][$ds_num]["max_cutoff"] = $rra[$rra_num][$ds_num]["average"] + ($stddev * $rra[$rra_num][$ds_num]["standard_deviation"]);
+ if ($rra[$rra_num][$ds_num]["max_cutoff"] > $ds_max[$ds_num]) {
+ $rra[$rra_num][$ds_num]["max_cutoff"] = $ds_max[$ds_num];
+ }
+
+ $rra[$rra_num][$ds_num]["numnksamples"] = 0;
+ $rra[$rra_num][$ds_num]["sumnksamples"] = 0;
+ $rra[$rra_num][$ds_num]["avgnksamples"] = 0;
+
+ /* go through values and find cutoffs */
+ $rra[$rra_num][$ds_num]["stddev_killed"] = 0;
+ $rra[$rra_num][$ds_num]["variance_killed"] = 0;
+
+ if (sizeof($samples[$rra_num][$ds_num])) {
+ foreach($samples[$rra_num][$ds_num] as $sample) {
+ if (($sample > $rra[$rra_num][$ds_num]["max_cutoff"]) ||
+ ($sample < $rra[$rra_num][$ds_num]["min_cutoff"])) {
+ debug(sprintf("Std Kill: Value '%.4e', StandardDev '%.4e', StdDevLimit '%.4e'", $sample, $rra[$rra_num][$ds_num]["standard_deviation"], ($rra[$rra_num][$ds_num]["max_cutoff"] * (1+$percent))));
+ $rra[$rra_num][$ds_num]["stddev_killed"]++;
+ $std_kills = true;
+ }else{
+ $rra[$rra_num][$ds_num]["numnksamples"]++;
+ $rra[$rra_num][$ds_num]["sumnksamples"] += $sample;
+ }
+
+ if ($rra[$rra_num][$ds_num]["variance_avg"] == "NAN") {
+ /* not enought samples to calculate */
+ }else if ($sample > ($rra[$rra_num][$ds_num]["variance_avg"] * (1+$percent))) {
+ /* kill based upon variance */
+ debug(sprintf("Var Kill: Value '%.4e', VarianceDev '%.4e', VarianceLimit '%.4e'", $sample, $rra[$rra_num][$ds_num]["variance_avg"], ($rra[$rra_num][$ds_num]["variance_avg"] * (1+$percent))));
+ $rra[$rra_num][$ds_num]["variance_killed"]++;
+ $var_kills = true;
+ }
+ }
+ }
+
+ if ($rra[$rra_num][$ds_num]["numnksamples"] > 0) {
+ $rra[$rra_num][$ds_num]["avgnksamples"] = $rra[$rra_num][$ds_num]["sumnksamples"] / $rra[$rra_num][$ds_num]["numnksamples"];
+ }
+ }else{
+ $rra[$rra_num][$ds_num]["standard_deviation"] = "N/A";
+ $rra[$rra_num][$ds_num]["average"] = "N/A";
+ $rra[$rra_num][$ds_num]["min_cutoff"] = "N/A";
+ $rra[$rra_num][$ds_num]["max_cutoff"] = "N/A";
+ $rra[$rra_num][$ds_num]["numnksamples"] = "N/A";
+ $rra[$rra_num][$ds_num]["sumnksamples"] = "N/A";
+ $rra[$rra_num][$ds_num]["avgnksamples"] = "N/A";
+ $rra[$rra_num][$ds_num]["stddev_killed"] = "N/A";
+ $rra[$rra_num][$ds_num]["variance_killed"] = "N/A";
+ $rra[$rra_num][$ds_num]["stddev_killed"] = "N/A";
+ $rra[$rra_num][$ds_num]["numnksamples"] = "N/A";
+ $rra[$rra_num][$ds_num]["sumnksamples"] = "N/A";
+ $rra[$rra_num][$ds_num]["variance_killed"] = "N/A";
+ $rra[$rra_num][$ds_num]["avgnksamples"] = "N/A";
+ }
+
+ $ds_num++;
+ }
+ }
+
+ $rra_num++;
+ }
+ }
+}
+
+function outputStatistics($rra) {
+ global $rra_cf, $rra_name, $ds_name, $rra_pdp, $html;
+
+ if (sizeof($rra)) {
+ if (!$html) {
+ echo "\n";
+ printf("%10s %16s %10s %7s %7s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s\n",
+ "Size", "DataSource", "CF", "Samples", "NonNan", "Avg", "StdDev",
+ "MaxValue", "MinValue", "MaxStdDev", "MinStdDev", "StdKilled", "VarKilled", "StdDevAvg", "VarAvg");
+ printf("%10s %16s %10s %7s %7s %10s %10s %10s %10s %10s %10s %10s %10s %10s %10s\n",
+ "----------", "---------------", "----------", "-------", "-------", "----------", "----------", "----------",
+ "----------", "----------", "----------", "----------", "----------", "----------",
+ "----------");
+ foreach($rra as $rra_key => $dses) {
+ if (sizeof($dses)) {
+ foreach($dses as $dskey => $ds) {
+ printf("%10s %16s %10s %7s %7s " .
+ ($ds["average"] < 1E6 ? "%10s ":"%10.4e ") .
+ ($ds["standard_deviation"] < 1E6 ? "%10s ":"%10.4e ") .
+ (isset($ds["max_value"]) ? ($ds["max_value"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") .
+ (isset($ds["min_value"]) ? ($ds["min_value"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") .
+ (isset($ds["max_cutoff"]) ? ($ds["max_cutoff"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") .
+ (isset($ds["min_cutoff"]) ? ($ds["min_cutoff"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") .
+ "%10s %10s " .
+ (isset($ds["avgnksampled"]) ? ($ds["avgnksamples"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") .
+ (isset($ds["variance_avg"]) ? ($ds["variance_avg"] < 1E6 ? "%10s ":"%10.4e ") : "%10s ") . "\n",
+ displayTime($rra_pdp[$rra_key]),
+ $ds_name[$dskey],
+ $rra_cf[$rra_key],
+ $ds["totalsamples"],
+ (isset($ds["numsamples"]) ? $ds["numsamples"] : "0"),
+ ($ds["average"] != "N/A" ? round($ds["average"],2) : $ds["average"]),
+ ($ds["standard_deviation"] != "N/A" ? round($ds["standard_deviation"],2) : $ds["standard_deviation"]),
+ (isset($ds["max_value"]) ? round($ds["max_value"],2) : "N/A"),
+ (isset($ds["min_value"]) ? round($ds["min_value"],2) : "N/A"),
+ ($ds["max_cutoff"] != "N/A" ? round($ds["max_cutoff"],2) : $ds["max_cutoff"]),
+ ($ds["min_cutoff"] != "N/A" ? round($ds["min_cutoff"],2) : $ds["min_cutoff"]),
+ $ds["stddev_killed"],
+ $ds["variance_killed"],
+ ($ds["avgnksamples"] != "N/A" ? round($ds["avgnksamples"],2) : $ds["avgnksamples"]),
+ (isset($ds["variance_avg"]) ? round($ds["variance_avg"],2) : "N/A"));
+ }
+ }
+ }
+
+ echo "\n";
+ }else{
+ printf("| %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s | %s |
\n",
+ "Size", "DataSource", "CF", "Samples", "NonNan", "Avg", "StdDev",
+ "MaxValue", "MinValue", "MaxStdDev", "MinStdDev", "StdKilled", "VarKilled", "StdDevAvg", "VarAvg");
+ foreach($rra as $rra_key => $dses) {
+ if (sizeof($dses)) {
+ foreach($dses as $dskey => $ds) {
+ printf("| %s | %s | %s | %s | %s | " .
+ ($ds["average"] < 1E6 ? "%s | ":"%.4e | ") .
+ ($ds["standard_deviation"] < 1E6 ? "%s | ":"%.4e | ") .
+ (isset($ds["max_value"]) ? ($ds["max_value"] < 1E6 ? "%s | ":"%.4e | ") : "%s | ") .
+ (isset($ds["min_value"]) ? ($ds["min_value"] < 1E6 ? "%s | ":"%.4e | ") : "%s | ") .
+ (isset($ds["max_cutoff"]) ? ($ds["max_cutoff"] < 1E6 ? "%s | ":"%.4e | ") : "%s | ") .
+ (isset($ds["min_cutoff"]) ? ($ds["min_cutoff"] < 1E6 ? "%s | ":"%.4e | ") : "%s | ") .
+ "%s | %s | " .
+ (isset($ds["avgnksampled"]) ? ($ds["avgnksamples"] < 1E6 ? "%s | ":"%.4e | ") : "%s | ") .
+ (isset($ds["variance_avg"]) ? ($ds["variance_avg"] < 1E6 ? "%s |
\n":"%.4e\n") : "%s\n") . "\n",
+ displayTime($rra_pdp[$rra_key]),
+ $ds_name[$dskey],
+ $rra_cf[$rra_key],
+ $ds["totalsamples"],
+ (isset($ds["numsamples"]) ? $ds["numsamples"] : "0"),
+ ($ds["average"] != "N/A" ? round($ds["average"],2) : $ds["average"]),
+ ($ds["standard_deviation"] != "N/A" ? round($ds["standard_deviation"],2) : $ds["standard_deviation"]),
+ (isset($ds["max_value"]) ? round($ds["max_value"],2) : "N/A"),
+ (isset($ds["min_value"]) ? round($ds["min_value"],2) : "N/A"),
+ ($ds["max_cutoff"] != "N/A" ? round($ds["max_cutoff"],2) : $ds["max_cutoff"]),
+ ($ds["min_cutoff"] != "N/A" ? round($ds["min_cutoff"],2) : $ds["min_cutoff"]),
+ $ds["stddev_killed"],
+ $ds["variance_killed"],
+ ($ds["avgnksamples"] != "N/A" ? round($ds["avgnksamples"],2) : $ds["avgnksamples"]),
+ (isset($ds["variance_avg"]) ? round($ds["variance_avg"],2) : "N/A"));
+ }
+ }
+ }
+ }
+ }
+}
+
+function updateXML(&$output, &$rra) {
+ global $numspike, $percent, $avgnan, $method, $total_kills;
+
+ /* variance subroutine */
+ $rra_num = 0;
+ $ds_num = 0;
+ $kills = 0;
+
+ if (sizeof($output)) {
+ foreach($output as $line) {
+ if (substr_count($line, "")) {
+ $linearray = explode("", $line);
+ /* discard the row */
+ array_shift($linearray);
+
+ /* initialize variables */
+ $ds_num = 0;
+ $out_row = "";
+ foreach($linearray as $dsvalue) {
+ /* peel off garbage */
+ $dsvalue = trim(str_replace("
", "", str_replace("", "", $dsvalue)));
+ if (strtolower($dsvalue) == "nan") {
+ /* do nothing, it's a NaN */
+ }else{
+ if ($method == 2) {
+ if ($dsvalue > (1+$percent)*$rra[$rra_num][$ds_num]["variance_avg"]) {
+ if ($kills < $numspike) {
+ if ($avgnan == "avg") {
+ $dsvalue = $rra[$rra_num][$ds_num]["variance_avg"];
+ }else{
+ $dsvalue = "NaN";
+ }
+ $kills++;
+ $total_kills++;
+ }
+ }
+ }else{
+ if (($dsvalue > $rra[$rra_num][$ds_num]["max_cutoff"]) ||
+ ($dsvalue < $rra[$rra_num][$ds_num]["min_cutoff"])) {
+ if ($kills < $numspike) {
+ if ($avgnan == "avg") {
+ $dsvalue = $rra[$rra_num][$ds_num]["average"];
+ }else{
+ $dsvalue = "NaN";
+ }
+ $kills++;
+ $total_kills++;
+ }
+ }
+ }
+ }
+
+ $out_row .= " " . $dsvalue . "";
+ $ds_num++;
+ }
+
+ $out_row .= "";
+
+ $new_array[] = $out_row;
+ }else{
+ if (substr_count($line, "")) {
+ $ds_minmax = array();
+ $rra_num++;
+ $kills = 0;
+ }else if (substr_count($line, "")) {
+ $ds_num++;
+ $kills = 0;
+ }
+
+ $new_array[] = $line;
+ }
+ }
+ }
+
+ return $new_array;
+}
+
+function removeComments(&$output) {
+ if (sizeof($output)) {
+ foreach($output as $line) {
+ $line = trim($line);
+ if ($line == "") {
+ continue;
+ }else{
+ /* is there a comment, remove it */
+ $comment_start = strpos($line, "");
+ if ($comment_start == 0) {
+ $line = trim(substr($line, $comment_end+3));
+ }else{
+ $line = trim(substr($line,0,$comment_start-1) . substr($line,$comment_end+3));
+ }
+ }
+
+ if ($line != "") {
+ $new_array[] = $line;
+ }
+ }
+ }
+ /* transfer the new array back to the original array */
+ return $new_array;
+ }
+}
+
+function displayTime($pdp) {
+ global $step;
+
+ $total_time = $pdp * $step; // seconds
+
+ if ($total_time < 60) {
+ return $total_time . " secs";
+ }else{
+ $total_time = $total_time / 60;
+
+ if ($total_time < 60) {
+ return $total_time . " mins";
+ }else{
+ $total_time = $total_time / 60;
+
+ if ($total_time < 24) {
+ return $total_time . " hours";
+ }else{
+ $total_time = $total_time / 24;
+
+ return $total_time . " days";
+ }
+ }
+ }
+}
+
+function debug($string) {
+ global $debug;
+
+ if ($debug) {
+ echo "DEBUG: " . $string . "\n";
+ }
+}
+
+function standard_deviation($samples) {
+ $sample_count = count($samples);
+
+ for ($current_sample = 0; $sample_count > $current_sample; ++$current_sample) {
+ $sample_square[$current_sample] = pow($samples[$current_sample], 2);
+ }
+
+ return sqrt(array_sum($sample_square) / $sample_count - pow((array_sum($samples) / $sample_count), 2));
+}
+
+/* display_help - displays the usage of the function */
+function display_help () {
+ global $using_cacti;
+
+ if ($using_cacti) {
+ $version = spikekill_version();
+ }else{
+ $version = "v1.0";
+ }
+
+ echo "Cacti Spike Remover " . ($using_cacti ? "v" . $version["version"] : $version) . ", Copyright 2009, The Cacti Group, Inc.\n\n";
+ echo "Usage:\n";
+ echo "removespikes.php -R|--rrdfile=rrdfile [-M|--method=stddev] [-A|--avgnan] [-S|--stddev=N]\n";
+ echo " [-P|--percent=N] [-N|--number=N] [-D|--dryrun] [-d|--debug] [-h|--help|-v|-V|--version]\n\n";
+
+ echo "The RRDfile input parameter is mandatory. If no other input parameters are specified the defaults\n";
+ echo "are taken from the Spikekill Plugin settings.\n\n";
+
+ echo "-M|--method - The spike removal method to use. Options are 'stddev'|'variance'\n";
+ echo "-A|--avgnan - The spike replacement method to use. Options are 'avg'|'nan'\n";
+ echo "-S|--stddev - The number of standard deviations +/- allowed\n";
+ echo "-P|--percent - The sample to sample percentage variation allowed\n";
+ echo "-N|--number - The maximum number of spikes to remove from the RRDfile\n";
+ echo "-D|--dryrun - If specified, the RRDfile will not be changed. Instead a summary of\n";
+ echo " changes that would have been performed will be issued.\n\n";
+
+ echo "The remainder of arguments are informational\n";
+ echo "-d|--debug - Display verbose output during execution\n";
+ echo "-v -V --version - Display this help message\n";
+ echo "-h --help - display this help message\n";
+}