From 172e75102f3c8a2b0b9dd9652653750cec76c5d9 Mon Sep 17 00:00:00 2001 From: raviks789 <33730024+raviks789@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:54:15 +0200 Subject: [PATCH] Do not evaluate invalid performance data --- library/Icingadb/Util/PerfData.php | 97 +++++++++++++++---- library/Icingadb/Util/PerfDataFormat.php | 3 +- library/Icingadb/Util/ThresholdRange.php | 33 +++++++ .../Icingadb/Widget/Detail/PerfDataTable.php | 19 +++- public/css/widget/performance-data-table.less | 5 + 5 files changed, 134 insertions(+), 23 deletions(-) diff --git a/library/Icingadb/Util/PerfData.php b/library/Icingadb/Util/PerfData.php index 89cdad32..cc33d165 100644 --- a/library/Icingadb/Util/PerfData.php +++ b/library/Icingadb/Util/PerfData.php @@ -39,21 +39,21 @@ class PerfData /** * The value * - * @var float + * @var ?float */ protected $value; /** * The minimum value * - * @var float + * @var ?float */ protected $minValue; /** * The maximum value * - * @var float + * @var ?float */ protected $maxValue; @@ -71,6 +71,27 @@ class PerfData */ protected $criticalThreshold; + /** + * The raw value + * + * @var ?string + */ + protected $rawValue; + + /** + * The raw minimum value + * + * @var ?string + */ + protected $rawMinValue; + + /** + * The raw maximum value + * + * @var ?string + */ + protected $rawMaxValue; + /** * Create a new PerfData object based on the given performance data label and value * @@ -312,7 +333,7 @@ class PerfData */ public function isVisualizable(): bool { - return isset($this->minValue) && isset($this->maxValue) && isset($this->value); + return isset($this->minValue, $this->maxValue, $this->value) && $this->isValid(); } /** @@ -428,25 +449,39 @@ class PerfData $matches = array(); if (preg_match('@^(U|-?(?:\d+)?(?:\.\d+)?)([a-zA-TV-Z%°]{1,3})$@u', $parts[0], $matches)) { $this->unit = $matches[2]; - $this->value = $matches[1]; + $value = $matches[1]; } else { - $this->value = $parts[0]; + $value = $parts[0]; } - if (! is_numeric($this->value)) { + if (! is_numeric($value)) { + if ($value !== 'U') { + $this->rawValue = $parts[0]; + } + $this->value = null; + } else { + $this->value = floatval($value); } switch (count($parts)) { /* @noinspection PhpMissingBreakStatementInspection */ case 5: if ($parts[4] !== '') { - $this->maxValue = $parts[4]; + if (is_numeric($parts[4])) { + $this->maxValue = floatval($parts[4]); + } else { + $this->rawMaxValue = $parts[4]; + } } /* @noinspection PhpMissingBreakStatementInspection */ case 4: if ($parts[3] !== '') { - $this->minValue = $parts[3]; + if (is_numeric($parts[3])) { + $this->minValue = floatval($parts[3]); + } else { + $this->rawMinValue = $parts[3]; + } } /* @noinspection PhpMissingBreakStatementInspection */ case 3: @@ -509,6 +544,10 @@ class PerfData } if ($value instanceof ThresholdRange) { + if (! $value->isValid()) { + return (string) $value; + } + if ($value->getMin()) { return (string) $value; } @@ -578,18 +617,22 @@ class PerfData public function toArray(): array { - return array( + return [ 'label' => $this->getLabel(), - 'value' => $this->format($this->getValue()), - 'min' => isset($this->minValue) && !$this->isPercentage() - ? $this->format($this->minValue) - : '', - 'max' => isset($this->maxValue) && !$this->isPercentage() - ? $this->format($this->maxValue) - : '', - 'warn' => $this->format($this->warningThreshold), - 'crit' => $this->format($this->criticalThreshold) - ); + 'value' => isset($this->value) ? $this->format($this->value) : $this->rawValue, + 'min' => (string) ( + ! $this->isPercentage() + ? (isset($this->minValue) ? $this->format($this->minValue) : $this->rawMinValue) + : null + ), + 'max' => (string) ( + ! $this->isPercentage() + ? (isset($this->maxValue) ? $this->format($this->maxValue) : $this->rawMaxValue) + : null + ), + 'warn' => $this->format($this->warningThreshold), + 'crit' => $this->format($this->criticalThreshold) + ]; } /** @@ -643,4 +686,18 @@ class PerfData return false; } + + /** + * Returns whether the performance data can be evaluated + * + * @return bool + */ + public function isValid(): bool + { + return ! isset($this->rawValue) + && ! isset($this->rawMinValue) + && ! isset($this->rawMaxValue) + && $this->criticalThreshold->isValid() + && $this->warningThreshold->isValid(); + } } diff --git a/library/Icingadb/Util/PerfDataFormat.php b/library/Icingadb/Util/PerfDataFormat.php index 8c33ae5c..1caffff0 100644 --- a/library/Icingadb/Util/PerfDataFormat.php +++ b/library/Icingadb/Util/PerfDataFormat.php @@ -131,10 +131,9 @@ class PerfDataFormat return sprintf('%0.2f d', $value / 86400); } - protected static function formatForUnits($value, array &$units, int $base): string + protected static function formatForUnits(float $value, array &$units, int $base): string { $sign = ''; - $value = (float) $value; if ($value < 0) { $value = abs($value); $sign = '-'; diff --git a/library/Icingadb/Util/ThresholdRange.php b/library/Icingadb/Util/ThresholdRange.php index fefed75b..675697a5 100644 --- a/library/Icingadb/Util/ThresholdRange.php +++ b/library/Icingadb/Util/ThresholdRange.php @@ -37,6 +37,13 @@ class ThresholdRange */ protected $raw; + /** + * Whether the threshold range is valid + * + * @var bool + */ + protected $isValid = true; + /** * Create a new instance based on a threshold range conforming to * @@ -61,6 +68,12 @@ class ThresholdRange if (strpos($rawRange, ':') === false) { $min = 0.0; + $max = trim($rawRange); + if (! is_numeric($max)) { + $range->isValid = false; + return $range; + } + $max = floatval(trim($rawRange)); } else { list($min, $max) = explode(':', $rawRange, 2); @@ -75,9 +88,19 @@ class ThresholdRange $min = null; break; default: + if (! is_numeric($min)) { + $range->isValid = false; + return $range; + } + $min = floatval($min); } + if (! empty($max) && ! is_numeric($max)) { + $range->isValid = false; + return $range; + } + $max = empty($max) ? null : floatval($max); } @@ -168,6 +191,16 @@ class ThresholdRange )); } + /** + * Return whether the threshold range is valid + * + * @return bool + */ + public function isValid() + { + return $this->isValid; + } + /** * Return the textual representation of $this, suitable for fromString() * diff --git a/library/Icingadb/Widget/Detail/PerfDataTable.php b/library/Icingadb/Widget/Detail/PerfDataTable.php index a4417a87..957c9cac 100644 --- a/library/Icingadb/Widget/Detail/PerfDataTable.php +++ b/library/Icingadb/Widget/Detail/PerfDataTable.php @@ -12,9 +12,13 @@ use ipl\Html\HtmlElement; use ipl\Html\HtmlString; use ipl\Html\Table; use ipl\Html\Text; +use ipl\I18n\Translation; +use ipl\Web\Widget\Icon; class PerfDataTable extends Table { + use Translation; + protected $defaultAttributes = [ 'class' => 'performance-data-table collapsible', 'data-visible-rows' => 6 @@ -58,7 +62,7 @@ class PerfDataTable extends Table $containsSparkline = false; foreach ($pieChartData as $perfdata) { - if ($perfdata->isVisualizable()) { + if ($perfdata->isVisualizable() || ! $perfdata->isValid()) { $containsSparkline = true; break; } @@ -91,6 +95,19 @@ class PerfDataTable extends Table HtmlString::create($perfdata->asInlinePie($this->color)->render()), ['class' => 'sparkline-col'] ); + } elseif (! $perfdata->isValid()) { + $cols[] = Table::td( + new Icon( + 'triangle-exclamation', + [ + 'title' => $this->translate( + 'Evaluation failed. Performance data is invalid.' + ), + 'class' => ['invalid-perfdata'] + ] + ), + ['class' => 'sparkline-col'] + ); } else { $cols[] = Table::td(''); } diff --git a/public/css/widget/performance-data-table.less b/public/css/widget/performance-data-table.less index a1a7b6ee..26c18c83 100644 --- a/public/css/widget/performance-data-table.less +++ b/public/css/widget/performance-data-table.less @@ -43,6 +43,11 @@ min-width: 1.75em; width: 1.75em; padding: 2/12em 0; + .invalid-perfdata { + font-size: 1.25em; + vertical-align: text-bottom; + color: @color-warning; + } } .inline-pie > svg {