mirror of
https://github.com/Icinga/icingaweb2.git
synced 2025-09-23 01:47:54 +02:00
Inline pie charts usually don't contain that much data and the SVG should be rather small in size. So it doesn't hurt rendering them instantly.
547 lines
14 KiB
PHP
547 lines
14 KiB
PHP
<?php
|
|
/* Icinga Web 2 | (c) 2013 Icinga Development Team | GPLv2+ */
|
|
|
|
namespace Icinga\Module\Monitoring\Plugin;
|
|
|
|
use Icinga\Util\Format;
|
|
use InvalidArgumentException;
|
|
use Icinga\Exception\ProgrammingError;
|
|
use Icinga\Web\Widget\Chart\InlinePie;
|
|
use Icinga\Module\Monitoring\Object\Service;
|
|
use Zend_Controller_Front;
|
|
|
|
class Perfdata
|
|
{
|
|
const PERFDATA_OK = 'ok';
|
|
const PERFDATA_WARNING = 'warning';
|
|
const PERFDATA_CRITICAL = 'critical';
|
|
|
|
/**
|
|
* The performance data value being parsed
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $perfdataValue;
|
|
|
|
/**
|
|
* Unit of measurement (UOM)
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $unit;
|
|
|
|
/**
|
|
* The label
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $label;
|
|
|
|
/**
|
|
* The value
|
|
*
|
|
* @var float
|
|
*/
|
|
protected $value;
|
|
|
|
/**
|
|
* The minimum value
|
|
*
|
|
* @var float
|
|
*/
|
|
protected $minValue;
|
|
|
|
/**
|
|
* The maximum value
|
|
*
|
|
* @var float
|
|
*/
|
|
protected $maxValue;
|
|
|
|
/**
|
|
* The WARNING threshold
|
|
*
|
|
* @var ThresholdRange
|
|
*/
|
|
protected $warningThreshold;
|
|
|
|
/**
|
|
* The CRITICAL threshold
|
|
*
|
|
* @var ThresholdRange
|
|
*/
|
|
protected $criticalThreshold;
|
|
|
|
/**
|
|
* Create a new Perfdata object based on the given performance data label and value
|
|
*
|
|
* @param string $label The perfdata label
|
|
* @param string $value The perfdata value
|
|
*/
|
|
public function __construct($label, $value)
|
|
{
|
|
$this->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,2})$@u', $parts[0], $matches)) {
|
|
$this->unit = strtolower($matches[3]);
|
|
$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 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);
|
|
}
|
|
|
|
/**
|
|
* Format the title string that represents this perfdata set
|
|
*
|
|
* @param bool $html
|
|
*
|
|
* @return string
|
|
*/
|
|
public function formatLabel($html = false)
|
|
{
|
|
return sprintf(
|
|
$html ? '<b>%s %s</b> (%s%%)' : '%s %s (%s%%)',
|
|
htmlspecialchars($this->getLabel()),
|
|
$this->format($this->value),
|
|
number_format($this->getPercentage(), 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;
|
|
}
|
|
}
|