Skip to content

Commit

Permalink
Resolving Issue #2664
Browse files Browse the repository at this point in the history
- Cacti spikekill should have a rrd backup cleanup process
- Also resolved false positive leading to excessive backps
  • Loading branch information
cigamit committed May 5, 2019
1 parent 614bc3e commit 77332b6
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 38 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Cacti CHANGELOG
-feature#2539: New global setting to add permanent unlock of graphs
-feature#2540: New user setting to forget graph tree history on tab close
-feature#2654: New hook to notify plugins of user profile changes ('auth_profile_update_data')
-feature#2664: Cacti spikekill should have a rrd backup cleanup process
-feature: Make html_split_string UTF-8 compatible

1.2.3
Expand Down
10 changes: 9 additions & 1 deletion include/global_settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,15 @@
'method' => 'drop_multi',
'description' => __('When performing batch spike removal, only the templates selected below will be acted on.'),
'array' => $spikekill_templates,
)
),
'spikekill_purge' => array(
'friendly_name' => __('Backup Retention'),
'description' => __('When SpikeKill kills spikes in graphs, it makes a backup of the RRDfile. How long should these backup files be retained?'),
'method' => 'drop_array',
'default' => '7856352',
'none_value' => __('Never'),
'array' => $timespans
)
)
);

Expand Down
66 changes: 39 additions & 27 deletions lib/spikekill.php
Original file line number Diff line number Diff line change
Expand Up @@ -626,7 +626,7 @@ public function remove_spikes() {

/* finally update the file XML file and Reprocess the RRDfile */
if (!$this->dryrun) {
if ($this->total_kills) {
if (($this->method == 1 && $this->var_kills) || ($this->method == 2 && $this->std_kills) || ($this->method > 2 && $this->total_kills > 0)) {
if ($this->writeXMLFile($new_output, $xmlfile)) {
if ($this->backupRRDFile($this->rrdfile)) {
$this->createRRDFileFromXML($xmlfile, $this->rrdfile);
Expand Down Expand Up @@ -813,10 +813,12 @@ private function calculateOverallStatistics(&$rra, &$samples) {
}
} elseif (($sample > $rra[$rra_num][$ds_num]['max_cutoff']) ||
($sample < $rra[$rra_num][$ds_num]['min_cutoff'])) {
$this->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+$this->percent))));
if ($this->method == 2) {
$this->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+$this->percent))));

$rra[$rra_num][$ds_num]['stddev_killed']++;
$this->std_kills = true;
$rra[$rra_num][$ds_num]['stddev_killed']++;
$this->std_kills = true;
}
} elseif (is_numeric($sample)) {
$rra[$rra_num][$ds_num]['numnksamples']++;
$rra[$rra_num][$ds_num]['sumnksamples'] += $sample;
Expand All @@ -827,11 +829,13 @@ private function calculateOverallStatistics(&$rra, &$samples) {
} elseif ($rra[$rra_num][$ds_num]['variance_avg'] == 'NAN') {
/* not enought samples to calculate */
} elseif ($sample > ($rra[$rra_num][$ds_num]['variance_avg'] * (1+$this->percent))) {
/* kill based upon variance */
$this->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+$this->percent))));
if ($this->method == 1) {
/* kill based upon variance */
$this->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+$this->percent))));

$rra[$rra_num][$ds_num]['variance_killed']++;
$this->var_kills = true;
$rra[$rra_num][$ds_num]['variance_killed']++;
$this->var_kills = true;
}
}
}
}
Expand Down Expand Up @@ -991,66 +995,73 @@ private function updateXML(&$output, &$rra) {
if ($this->method == 3) {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['variance_avg']);
$kills++;
$this->total_kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$kills++;
$this->total_kills++;
}

$kills++;
$this->total_kills++;
} elseif ($this->method == 4) {
if ($dsvalue > (1+$this->percent)*$rra[$rra_num][$ds_num]['variance_avg'] || strtolower($dsvalue) == 'nan') {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['variance_avg']);
$kills++;
$this->total_kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$kills++;
$this->total_kills++;
}

$kills++;
$this->total_kills++;
}
}
} elseif(strtolower($dsvalue) == 'nan' && isset($last_num[$ds_num])) {
if ($this->method == 2) {
if ($kills < $this->numspike) {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['variance_avg']);
$this->total_kills++;
$kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$this->total_kills++;
$kills++;
} else {
$dsvalue = 'NaN';
}

$this->total_kills++;
$kills++;
}
} else {
} elseif ($last_num[$ds_num] != 0) {
if ($kills < $this->numspike) {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['average']);
$this->total_kills++;
$kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$this->total_kills++;
$kills++;
} else {
$dsvalue = 'NaN';
}

$this->total_kills++;
$kills++;
}
} else {
$dsvalue = 'NaN';
}
} else {
if ($this->method == 2) {
if ($dsvalue > (1+$this->percent)*$rra[$rra_num][$ds_num]['variance_avg']) {
if ($kills < $this->numspike) {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['variance_avg']);
$kills++;
$this->total_kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$kills++;
$this->total_kills++;
} else {
$dsvalue = 'NaN';
}

$kills++;
$this->total_kills++;
}
} else {
$last_num[$ds_num] = $dsvalue;
Expand All @@ -1061,14 +1072,15 @@ private function updateXML(&$output, &$rra) {
if ($kills < $this->numspike) {
if ($this->avgnan == 'avg') {
$dsvalue = sprintf('%1.10e', $rra[$rra_num][$ds_num]['average']);
$kills++;
$this->total_kills++;
} elseif ($this->avgnan == 'last' && isset($last_num[$ds_num])) {
$dsvalue = $last_num[$ds_num];
$kills++;
$this->total_kills++;
} else {
$dsvalue = 'NaN';
}

$kills++;
$this->total_kills++;
}
} else {
$last_num[$ds_num] = $dsvalue;
Expand Down
70 changes: 60 additions & 10 deletions poller_spikekill.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@

if (!$templates) {
$templates = db_fetch_cell("SELECT value FROM settings WHERE name='spikekill_templates'");
$templates = explode(',', $templates);
if ($templates != '') {
$templates = explode(',', $templates);
}
} else {
$templates = explode(',', $templates);
}
Expand All @@ -106,15 +108,22 @@

$graphs = kill_spikes($templates, $kills);

$purges = 0;
if (read_config_option('spikekill_purge') > 0) {
$purges = purge_spike_backups();
}

list($micro,$seconds) = explode(' ', microtime());
$end = $seconds + $micro;

$cacti_stats = sprintf(
'Time:%01.4f ' .
'Graphs:%s ' .
'Purges:%s ' .
'Kills:%s',
round($end-$start,2),
$graphs,
$purges,
$kills);

/* log to the database */
Expand Down Expand Up @@ -173,6 +182,45 @@ function debug($message) {
}
}


function purge_spike_backups() {
$directory = read_config_option('spikekill_backupdir');
$retention = read_config_option('spikekil_backup');

$purges = 0;

if (empty($retention)) {
return false;
}

$earlytime = time() - $retention;

if ($directory != '' && s_dir($directory) && is_writable($directory)) {
$files = array_diff(scandir($directory), array('.', '..'));

if (cacti_sizeof($files)) {
foreach($files as $file) {
$filepath = $directory . '/' . $file;

if (is_file($filepath) && strpos($filepath, 'rrd') !== false) {
$mtime = filemtime($filepath);

if ($mtime < $earlytime) {
if (is_writable($filepath)) {
unlink($filepath);
$purges++;
} else {
cacti_log('Unable to remove ' . $filepath . ' due to write permissions', 'SPIKES');
}
}
}
}
}
}

return $purges;
}

function kill_spikes($templates, &$found) {
global $debug, $config;

Expand All @@ -186,16 +234,18 @@ function kill_spikes($templates, &$found) {
WHERE gt.id IN (' . implode(',', $templates) . ')'), 'rrd_path', 'rrd_path');

if (cacti_sizeof($rrdfiles)) {
foreach($rrdfiles as $f) {
debug("Removing Spikes from '$f'");
$response = exec(read_config_option('path_php_binary') . ' -q ' .
$config['base_path'] . '/cli/removespikes.php --rrdfile=' . $f . ($debug ? ' --debug':''));
if (substr_count($response, 'Spikes Found and Remediated')) {
$found++;
}
foreach($rrdfiles as $f) {
debug("Removing Spikes from '$f'");

debug(str_replace('NOTE: ', '', $response));
}
$response = exec(read_config_option('path_php_binary') . ' -q ' .
$config['base_path'] . '/cli/removespikes.php --rrdfile=' . $f . ($debug ? ' --debug':''));

if (substr_count($response, 'Spikes Found and Remediated')) {
$found++;
}

debug(str_replace('NOTE: ', '', $response));
}
}

return cacti_sizeof($rrdfiles);
Expand Down

0 comments on commit 77332b6

Please sign in to comment.