From 8ca6cc32b2c789a3149861159ad258f2cb9491e3 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 14:39:39 +0200 Subject: Adding upstream version 2.11.4. Signed-off-by: Daniel Baumann --- .../library/Monitoring/Plugin/Perfdata.php | 550 +++++++++++++++++++++ .../library/Monitoring/Plugin/PerfdataSet.php | 144 ++++++ .../library/Monitoring/Plugin/ThresholdRange.php | 179 +++++++ 3 files changed, 873 insertions(+) create mode 100644 modules/monitoring/library/Monitoring/Plugin/Perfdata.php create mode 100644 modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php create mode 100644 modules/monitoring/library/Monitoring/Plugin/ThresholdRange.php (limited to 'modules/monitoring/library/Monitoring/Plugin') diff --git a/modules/monitoring/library/Monitoring/Plugin/Perfdata.php b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php new file mode 100644 index 0000000..476354a --- /dev/null +++ b/modules/monitoring/library/Monitoring/Plugin/Perfdata.php @@ -0,0 +1,550 @@ +perfdataValue = $value; + $this->label = $label; + $this->parse(); + + if ($this->unit === '%') { + if ($this->minValue === null) { + $this->minValue = 0.0; + } + if ($this->maxValue === null) { + $this->maxValue = 100.0; + } + } + + $warn = $this->warningThreshold->getMax(); + if ($warn !== null) { + $crit = $this->criticalThreshold->getMax(); + if ($crit !== null && $warn > $crit) { + $this->warningThreshold->setInverted(); + $this->criticalThreshold->setInverted(); + } + } + } + + /** + * Return a new Perfdata object based on the given performance data key=value pair + * + * @param string $perfdata The key=value pair to parse + * + * @return Perfdata + * + * @throws InvalidArgumentException In case the given performance data has no content or a invalid format + */ + public static function fromString($perfdata) + { + if (empty($perfdata)) { + throw new InvalidArgumentException('Perfdata::fromString expects a string with content'); + } elseif (strpos($perfdata, '=') === false) { + throw new InvalidArgumentException( + 'Perfdata::fromString expects a key=value formatted string. Got "' . $perfdata . '" instead' + ); + } + + list($label, $value) = explode('=', $perfdata, 2); + return new static(trim($label), trim($value)); + } + + /** + * Return whether this performance data's value is a number + * + * @return bool True in case it's a number, otherwise False + */ + public function isNumber() + { + return $this->unit === null; + } + + /** + * Return whether this performance data's value are seconds + * + * @return bool True in case it's seconds, otherwise False + */ + public function isSeconds() + { + return in_array($this->unit, array('s', 'ms', 'us')); + } + + /** + * Return whether this performance data's value is a temperature + * + * @return bool True in case it's temperature, otherwise False + */ + public function isTemperature() + { + return in_array($this->unit, array('°c', '°f')); + } + + /** + * Return whether this performance data's value is in percentage + * + * @return bool True in case it's in percentage, otherwise False + */ + public function isPercentage() + { + return $this->unit === '%'; + } + + /** + * Return whether this performance data's value is in bytes + * + * @return bool True in case it's in bytes, otherwise False + */ + public function isBytes() + { + return in_array($this->unit, array('b', 'kb', 'mb', 'gb', 'tb')); + } + + /** + * Return whether this performance data's value is a counter + * + * @return bool True in case it's a counter, otherwise False + */ + public function isCounter() + { + return $this->unit === 'c'; + } + + /** + * Returns whether it is possible to display a visual representation + * + * @return bool True when the perfdata is visualizable + */ + public function isVisualizable() + { + return isset($this->minValue) && isset($this->maxValue) && isset($this->value); + } + + /** + * Return this perfomance data's label + */ + public function getLabel() + { + return $this->label; + } + + /** + * Return the value or null if it is unknown (U) + * + * @return null|float + */ + public function getValue() + { + return $this->value; + } + + /** + * Return the unit as a string + * + * @return string + */ + public function getUnit() + { + return $this->unit; + } + + /** + * Return the value as percentage (0-100) + * + * @return null|float + */ + public function getPercentage() + { + if ($this->isPercentage()) { + return $this->value; + } + + if ($this->maxValue !== null) { + $minValue = $this->minValue !== null ? $this->minValue : 0.0; + if ($this->maxValue == $minValue) { + return null; + } + + if ($this->value > $minValue) { + return (($this->value - $minValue) / ($this->maxValue - $minValue)) * 100; + } + } + } + + /** + * Return this performance data's warning treshold + * + * @return ThresholdRange + */ + public function getWarningThreshold() + { + return $this->warningThreshold; + } + + /** + * Return this performance data's critical treshold + * + * @return ThresholdRange + */ + public function getCriticalThreshold() + { + return $this->criticalThreshold; + } + + /** + * Return the minimum value or null if it is not available + * + * @return null|string + */ + public function getMinimumValue() + { + return $this->minValue; + } + + /** + * Return the maximum value or null if it is not available + * + * @return null|float + */ + public function getMaximumValue() + { + return $this->maxValue; + } + + /** + * Return this performance data as string + * + * @return string + */ + public function __toString() + { + return $this->formatLabel(); + } + + /** + * Parse the current performance data value + * + * @todo Handle optional min/max if UOM == % + */ + protected function parse() + { + $parts = explode(';', $this->perfdataValue); + + $matches = array(); + if (preg_match('@^(-?(?:\d+)?(?:\.\d+)?)([a-zA-Z%°]{1,3})$@u', $parts[0], $matches)) { + $this->unit = strtolower($matches[2]); + $this->value = self::convert($matches[1], $this->unit); + } else { + $this->value = self::convert($parts[0]); + } + + switch (count($parts)) { + /* @noinspection PhpMissingBreakStatementInspection */ + case 5: + if ($parts[4] !== '') { + $this->maxValue = self::convert($parts[4], $this->unit); + } + /* @noinspection PhpMissingBreakStatementInspection */ + case 4: + if ($parts[3] !== '') { + $this->minValue = self::convert($parts[3], $this->unit); + } + /* @noinspection PhpMissingBreakStatementInspection */ + case 3: + $this->criticalThreshold = self::convert( + ThresholdRange::fromString(trim($parts[2])), + $this->unit + ); + // Fallthrough + case 2: + $this->warningThreshold = self::convert( + ThresholdRange::fromString(trim($parts[1])), + $this->unit + ); + } + + if ($this->warningThreshold === null) { + $this->warningThreshold = new ThresholdRange(); + } + if ($this->criticalThreshold === null) { + $this->criticalThreshold = new ThresholdRange(); + } + } + + /** + * Return the given value converted to its smallest supported representation + * + * @param string $value The value to convert + * @param string $fromUnit The unit the value currently represents + * + * @return null|float Null in case the value is not a number + */ + protected static function convert($value, $fromUnit = null) + { + if ($value instanceof ThresholdRange) { + $value = clone $value; + + $min = $value->getMin(); + if ($min !== null) { + $value->setMin(self::convert($min, $fromUnit)); + } + + $max = $value->getMax(); + if ($max !== null) { + $value->setMax(self::convert($max, $fromUnit)); + } + + return $value; + } + + if (is_numeric($value)) { + switch ($fromUnit) { + case 'us': + return $value / pow(10, 6); + case 'ms': + return $value / pow(10, 3); + case 'tb': + return floatval($value) * pow(2, 40); + case 'gb': + return floatval($value) * pow(2, 30); + case 'mb': + return floatval($value) * pow(2, 20); + case 'kb': + return floatval($value) * pow(2, 10); + default: + return (float) $value; + } + } + } + + protected function calculatePieChartData() + { + $rawValue = $this->getValue(); + $minValue = $this->getMinimumValue() !== null ? $this->getMinimumValue() : 0; + $usedValue = ($rawValue - $minValue); + + $green = $orange = $red = 0; + + if ($this->criticalThreshold->contains($rawValue)) { + if ($this->warningThreshold->contains($rawValue)) { + $green = $usedValue; + } else { + $orange = $usedValue; + } + } else { + $red = $usedValue; + } + + return array($green, $orange, $red, ($this->getMaximumValue() - $minValue) - $usedValue); + } + + + public function asInlinePie() + { + if (! $this->isVisualizable()) { + throw new ProgrammingError('Cannot calculate piechart data for unvisualizable perfdata entry.'); + } + + $data = $this->calculatePieChartData(); + $pieChart = new InlinePie($data, $this); + $pieChart->setColors(array('#44bb77', '#ffaa44', '#ff5566', '#ddccdd')); + + return $pieChart; + } + + /** + * Format the given value depending on the currently used unit + */ + protected function format($value) + { + if ($value === null) { + return null; + } + + if ($value instanceof ThresholdRange) { + if ($value->getMin()) { + return (string) $value; + } + + $max = $value->getMax(); + return $max === null ? '' : $this->format($max); + } + + if ($this->isPercentage()) { + return (string)$value . '%'; + } + if ($this->isBytes()) { + return Format::bytes($value); + } + if ($this->isSeconds()) { + return Format::seconds($value); + } + if ($this->isTemperature()) { + return (string)$value . strtoupper($this->unit); + } + return number_format($value, 2) . ($this->unit !== null ? ' ' . $this->unit : ''); + } + + /** + * Format the title string that represents this perfdata set + * + * @param bool $html + * + * @return string + */ + public function formatLabel($html = false) + { + return sprintf( + $html ? '%s %s (%s%%)' : '%s %s (%s%%)', + htmlspecialchars($this->getLabel()), + $this->format($this->value), + number_format($this->getPercentage() ?? 0, 2) + ); + } + + public function toArray() + { + return array( + '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) + ); + } + + /** + * Return the state indicated by this perfdata + * + * @see Service + * + * @return int + */ + public function getState() + { + if ($this->value === null) { + return Service::STATE_UNKNOWN; + } + + if (! $this->criticalThreshold->contains($this->value)) { + return Service::STATE_CRITICAL; + } + + if (! $this->warningThreshold->contains($this->value)) { + return Service::STATE_WARNING; + } + + return Service::STATE_OK; + } + + /** + * Return whether the state indicated by this perfdata is worse than + * the state indicated by the other perfdata + * CRITICAL > UNKNOWN > WARNING > OK + * + * @param Perfdata $rhs the other perfdata + * + * @return bool + */ + public function worseThan(Perfdata $rhs) + { + if (($state = $this->getState()) === ($rhsState = $rhs->getState())) { + return $this->getPercentage() > $rhs->getPercentage(); + } + + if ($state === Service::STATE_CRITICAL) { + return true; + } + + if ($state === Service::STATE_UNKNOWN) { + return $rhsState !== Service::STATE_CRITICAL; + } + + if ($state === Service::STATE_WARNING) { + return $rhsState === Service::STATE_OK; + } + + return false; + } +} diff --git a/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php b/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php new file mode 100644 index 0000000..ef1ca0c --- /dev/null +++ b/modules/monitoring/library/Monitoring/Plugin/PerfdataSet.php @@ -0,0 +1,144 @@ +perfdataStr = $perfdataStr; + $this->parse(); + } + } + + /** + * Return a iterator for this set of performance data + * + * @return ArrayIterator + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->asArray()); + } + + /** + * Return a new set of performance data + * + * @param string $perfdataStr A space separated list of label/value pairs + * + * @return PerfdataSet + */ + public static function fromString($perfdataStr) + { + return new static($perfdataStr); + } + + /** + * Return this set of performance data as array + * + * @return array + */ + public function asArray() + { + return $this->perfdata; + } + + /** + * Parse the current performance data + */ + protected function parse() + { + while ($this->parserPos < strlen($this->perfdataStr)) { + $label = trim($this->readLabel()); + $value = trim($this->readUntil(' ')); + + if ($label) { + $this->perfdata[] = new Perfdata($label, $value); + } + } + } + + /** + * Return the next label found in the performance data + * + * @return string The label found + */ + protected function readLabel() + { + $this->skipSpaces(); + if (in_array($this->perfdataStr[$this->parserPos], array('"', "'"))) { + $quoteChar = $this->perfdataStr[$this->parserPos++]; + $label = $this->readUntil('='); + $this->parserPos++; + + if (($closingPos = strpos($label, $quoteChar)) > 0) { + $label = substr($label, 0, $closingPos); + } + } else { + $label = $this->readUntil('='); + $this->parserPos++; + } + + $this->skipSpaces(); + return $label; + } + + /** + * Return all characters between the current parser position and the given character + * + * @param string $stopChar The character on which to stop + * + * @return string + */ + protected function readUntil($stopChar) + { + $start = $this->parserPos; + while ($this->parserPos < strlen($this->perfdataStr) && $this->perfdataStr[$this->parserPos] !== $stopChar) { + $this->parserPos++; + } + + return substr($this->perfdataStr, $start, $this->parserPos - $start); + } + + /** + * Advance the parser position to the next non-whitespace character + */ + protected function skipSpaces() + { + while ($this->parserPos < strlen($this->perfdataStr) && $this->perfdataStr[$this->parserPos] === ' ') { + $this->parserPos++; + } + } +} diff --git a/modules/monitoring/library/Monitoring/Plugin/ThresholdRange.php b/modules/monitoring/library/Monitoring/Plugin/ThresholdRange.php new file mode 100644 index 0000000..bd27b8b --- /dev/null +++ b/modules/monitoring/library/Monitoring/Plugin/ThresholdRange.php @@ -0,0 +1,179 @@ + + * + * @param string $rawRange + * + * @return ThresholdRange + */ + public static function fromString($rawRange) + { + $range = new static(); + $range->raw = $rawRange; + + if ($rawRange == '') { + return $range; + } + + $rawRange = ltrim($rawRange); + if (substr($rawRange, 0, 1) === '@') { + $range->setInverted(); + $rawRange = substr($rawRange, 1); + } + + if (strpos($rawRange, ':') === false) { + $min = 0.0; + $max = floatval(trim($rawRange)); + } else { + list($min, $max) = explode(':', $rawRange, 2); + $min = trim($min); + $max = trim($max); + + switch ($min) { + case '': + $min = 0.0; + break; + case '~': + $min = null; + break; + default: + $min = floatval($min); + } + + $max = empty($max) ? null : floatval($max); + } + + return $range->setMin($min) + ->setMax($max); + } + + /** + * Set the smallest value inside the range (null stands for -∞) + * + * @param float|null $min + * + * @return $this + */ + public function setMin($min) + { + $this->min = $min; + return $this; + } + + /** + * Get the smallest value inside the range (null stands for -∞) + * + * @return float|null + */ + public function getMin() + { + return $this->min; + } + + /** + * Set the biggest value inside the range (null stands for ∞) + * + * @param float|null $max + * + * @return $this + */ + public function setMax($max) + { + $this->max = $max; + return $this; + } + + /** + * Get the biggest value inside the range (null stands for ∞) + * + * @return float|null + */ + public function getMax() + { + return $this->max; + } + + /** + * Set whether to invert the result of contains() + * + * @param bool $inverted + * + * @return $this + */ + public function setInverted($inverted = true) + { + $this->inverted = $inverted; + return $this; + } + + /** + * Get whether to invert the result of contains() + * + * @return bool + */ + public function isInverted() + { + return $this->inverted; + } + + /** + * Return whether $value is inside $this + * + * @param float $value + * + * @return bool + */ + public function contains($value) + { + return (bool) ($this->inverted ^ ( + ($this->min === null || $this->min <= $value) && ($this->max === null || $this->max >= $value) + )); + } + + /** + * Return the textual representation of $this, suitable for fromString() + * + * @return string + */ + public function __toString() + { + return (string) $this->raw; + } +} -- cgit v1.2.3