513 lines
14 KiB
PHP
513 lines
14 KiB
PHP
<?php
|
|
/* Icinga Web 2 | (c) 2013-2015 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
|
|
*
|
|
* TODO: Should be parsed Range-Object instead of string
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $warningThreshold;
|
|
|
|
/**
|
|
* The CRITICAL threshold
|
|
*
|
|
* TODO: Should be parsed Range-Object instead of string
|
|
*
|
|
* @var string
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 (false === strpos($perfdata, '=')) {
|
|
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 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 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;
|
|
if ((float) ($this->maxValue - $minValue) === 0.0) {
|
|
return null;
|
|
}
|
|
|
|
if ($this->value > $minValue) {
|
|
return (($this->value - $minValue) / ($this->maxValue - $minValue)) * 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return this performance data's warning treshold or null if it is not available
|
|
*
|
|
* @return null|string
|
|
*/
|
|
public function getWarningThreshold()
|
|
{
|
|
return $this->warningThreshold;
|
|
}
|
|
|
|
/**
|
|
* Return this performance data's critical treshold or null if it is not available
|
|
*
|
|
* @return null|string
|
|
*/
|
|
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})$@', $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))
|
|
{
|
|
case 5:
|
|
if ($parts[4] !== '') {
|
|
$this->maxValue = self::convert($parts[4], $this->unit);
|
|
}
|
|
case 4:
|
|
if ($parts[3] !== '') {
|
|
$this->minValue = self::convert($parts[3], $this->unit);
|
|
}
|
|
case 3:
|
|
$this->criticalThreshold = trim($parts[2]) ? trim($parts[2]) : null;
|
|
case 2:
|
|
$this->warningThreshold = trim($parts[1]) ? trim($parts[1]) : null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 (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;
|
|
$maxValue = $this->getMaximumValue();
|
|
$usedValue = ($rawValue - $minValue);
|
|
$unusedValue = ($maxValue - $minValue) - $usedValue;
|
|
|
|
$warningThreshold = $this->convert($this->warningThreshold, $this->unit);
|
|
$criticalThreshold = $this->convert($this->criticalThreshold, $this->unit);
|
|
|
|
$gray = $unusedValue;
|
|
$green = $orange = $red = 0;
|
|
|
|
$pieState = self::PERFDATA_OK;
|
|
if ($warningThreshold > $criticalThreshold) {
|
|
// inverted threshold parsing OK > warning > critical
|
|
if (isset($warningThreshold) && $this->value <= $warningThreshold) {
|
|
$pieState = self::PERFDATA_WARNING;
|
|
}
|
|
if (isset($criticalThreshold) && $this->value <= $criticalThreshold) {
|
|
$pieState = self::PERFDATA_CRITICAL;
|
|
}
|
|
|
|
} else {
|
|
// TODO: Use standard perfdata range format to decide the state #8194
|
|
|
|
// regular threshold parsing OK < warning < critical
|
|
if (isset($warningThreshold) && $rawValue > $warningThreshold) {
|
|
$pieState = self::PERFDATA_WARNING;
|
|
}
|
|
if (isset($criticalThreshold) && $rawValue > $criticalThreshold) {
|
|
$pieState = self::PERFDATA_CRITICAL;
|
|
}
|
|
}
|
|
|
|
switch ($pieState) {
|
|
case self::PERFDATA_OK:
|
|
$green = $usedValue;
|
|
break;
|
|
|
|
case self::PERFDATA_CRITICAL:
|
|
$red = $usedValue;
|
|
break;
|
|
|
|
case self::PERFDATA_WARNING:
|
|
$orange = $usedValue;
|
|
break;
|
|
}
|
|
|
|
return array($green, $orange, $red, $gray);
|
|
}
|
|
|
|
|
|
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'));
|
|
|
|
if (Zend_Controller_Front::getInstance()->getRequest()->isXmlHttpRequest()) {
|
|
$pieChart->disableNoScript();
|
|
}
|
|
return $pieChart;
|
|
}
|
|
|
|
/**
|
|
* Format the given value depending on the currently used unit
|
|
*/
|
|
protected function format($value)
|
|
{
|
|
if ($this->isPercentage()) {
|
|
return (string)$value . '%';
|
|
}
|
|
if ($this->isBytes()) {
|
|
return Format::bytes($value);
|
|
}
|
|
if ($this->isSeconds()) {
|
|
return Format::seconds($value);
|
|
}
|
|
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()
|
|
{
|
|
$parts = 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' => isset($this->warningThreshold) ? $this->format(self::convert($this->warningThreshold, $this->unit)) : '',
|
|
'crit' => isset($this->criticalThreshold) ? $this->format(self::convert($this->criticalThreshold, $this->unit)) : ''
|
|
);
|
|
return $parts;
|
|
}
|
|
|
|
/**
|
|
* 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 === null
|
|
|| $this->value < $this->criticalThreshold)) {
|
|
return Service::STATE_CRITICAL;
|
|
}
|
|
|
|
if (! ($this->warningThreshold === null
|
|
|| $this->value < $this->warningThreshold)) {
|
|
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;
|
|
}
|
|
}
|