mirror of
https://github.com/Icinga/icingaweb2-module-graphite.git
synced 2026-06-09 00:32:31 -04:00
parent
f3dd5c615c
commit
1c7dd2f46a
2 changed files with 399 additions and 0 deletions
304
library/Graphite/Graphing/Chart.php
Normal file
304
library/Graphite/Graphing/Chart.php
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Graphite\Graphing;
|
||||
|
||||
use DateTimeZone;
|
||||
use Icinga\Module\Graphite\GraphiteWebClientInterface;
|
||||
use Icinga\Module\Graphite\Util\MacroTemplate;
|
||||
use Icinga\Web\Response;
|
||||
use Icinga\Web\Url;
|
||||
use Icinga\Web\UrlParams;
|
||||
|
||||
class Chart
|
||||
{
|
||||
/**
|
||||
* Used to render the chart
|
||||
*
|
||||
* @var GraphiteWebClientInterface
|
||||
*/
|
||||
protected $graphiteWebClient;
|
||||
|
||||
/**
|
||||
* This chart's base
|
||||
*
|
||||
* @var Template
|
||||
*/
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* Target metrics by curve name
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected $metrics;
|
||||
|
||||
/**
|
||||
* The chart's begin
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $from = '-4hours';
|
||||
|
||||
/**
|
||||
* The chart's end
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $until;
|
||||
|
||||
/**
|
||||
* The chart's width
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $width = 300;
|
||||
|
||||
/**
|
||||
* The chart's height
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $height = 200;
|
||||
|
||||
/**
|
||||
* Whether to show the chart's legend
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $showLegend = true;
|
||||
|
||||
/**
|
||||
* The chart's time zone
|
||||
*
|
||||
* @var DateTimeZone
|
||||
*/
|
||||
protected $timeZone;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param GraphiteWebClientInterface $graphiteWebClient Used to render the chart
|
||||
* @param Template $template This chart's base
|
||||
* @param string[] $metrics Target metrics by curve name
|
||||
*/
|
||||
public function __construct(GraphiteWebClientInterface $graphiteWebClient, Template $template, array $metrics)
|
||||
{
|
||||
$this->graphiteWebClient = $graphiteWebClient;
|
||||
$this->template = $template;
|
||||
$this->metrics = $metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Let Graphite Web render this chart and serve the result immediately to the user agent (via the given response)
|
||||
*
|
||||
* Does not return.
|
||||
*
|
||||
* @param Response $response
|
||||
*/
|
||||
public function serveImage(Response $response)
|
||||
{
|
||||
$params = (new UrlParams())->addValues([
|
||||
'from' => $this->from,
|
||||
'width' => $this->width,
|
||||
'height' => $this->height,
|
||||
'hideLegend' => (string) ! $this->showLegend,
|
||||
'tz' => $this->timeZone->getName(),
|
||||
'_salt' => time() . '.000',
|
||||
'hideGrid' => 'true',
|
||||
'vTitle' => 'Percent',
|
||||
'lineMode' => 'connected',
|
||||
'xFormat' => '%a %H:%M',
|
||||
'drawNullAsZero' => 'false',
|
||||
'graphType' => 'line',
|
||||
'_ext' => 'whatever.svg'
|
||||
]);
|
||||
|
||||
if ($this->until !== null) {
|
||||
$params->set('until', $this->until);
|
||||
}
|
||||
|
||||
$variables = $this->getMetricVariables();
|
||||
|
||||
foreach ($this->template->getUrlParams() as $key => $value) {
|
||||
$params->set($key, $value->resolve($variables));
|
||||
}
|
||||
|
||||
$image = $this->graphiteWebClient->request('/render', $params, 'POST', [
|
||||
'Accept-language' => 'en',
|
||||
'Content-type' => 'application/x-www-form-urlencoded'
|
||||
]);
|
||||
|
||||
$response
|
||||
->setHeader('Content-Type', 'image/png', true)
|
||||
->setHeader('Content-Disposition', 'inline; filename="graph.png"', true)
|
||||
->setBody($image)
|
||||
->sendResponse();
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the values of the template's metrics filters' variables from the target metrics
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMetricVariables()
|
||||
{
|
||||
/** @var MacroTemplate[][] $curves */
|
||||
$curves = $this->template->getCurves();
|
||||
$variables = [];
|
||||
|
||||
foreach ($this->metrics as $curveName => $metric) {
|
||||
$vars = $curves[$curveName][0]->reverseResolve($metric);
|
||||
if ($vars !== false) {
|
||||
$variables = array_merge($variables, $vars);
|
||||
}
|
||||
}
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get begin
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set begin
|
||||
*
|
||||
* @param string $from
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setFrom($from)
|
||||
{
|
||||
$this->from = $from;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get end
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUntil()
|
||||
{
|
||||
return $this->until;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set end
|
||||
*
|
||||
* @param string $until
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setUntil($until)
|
||||
{
|
||||
$this->until = $until;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get width
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set width
|
||||
*
|
||||
* @param int $width
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setWidth($width)
|
||||
{
|
||||
$this->width = $width;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get height
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getHeight()
|
||||
{
|
||||
return $this->height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set height
|
||||
*
|
||||
* @param int $height
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeight($height)
|
||||
{
|
||||
$this->height = $height;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to show the chart's legend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getShowLegend()
|
||||
{
|
||||
return $this->showLegend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to show the chart's legend
|
||||
*
|
||||
* @param bool $showLegend
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setShowLegend($showLegend)
|
||||
{
|
||||
$this->showLegend = $showLegend;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time zone
|
||||
*
|
||||
* @return DateTimeZone
|
||||
*/
|
||||
public function getTimeZone()
|
||||
{
|
||||
return $this->timeZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time zone
|
||||
*
|
||||
* @param DateTimeZone $timeZone
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setTimeZone(DateTimeZone $timeZone)
|
||||
{
|
||||
$this->timeZone = $timeZone;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,101 @@ class Template
|
|||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all charts based on this template and applicable to the metrics
|
||||
* from the given data source restricted by the given filter
|
||||
*
|
||||
* @param MetricsDataSource $dataSource
|
||||
* @param string[] $filter
|
||||
*
|
||||
* @return Chart[]
|
||||
*/
|
||||
public function getCharts(MetricsDataSource $dataSource, array $filter)
|
||||
{
|
||||
$metrics = [];
|
||||
foreach ($this->curves as $curveName => $curve) {
|
||||
$query = $dataSource->select()->from($curve[0]);
|
||||
foreach ($filter as $key => $value) {
|
||||
$query->where($key, $value);
|
||||
}
|
||||
|
||||
foreach ($query->fetchColumn() as $metric) {
|
||||
$vars = $curve[0]->reverseResolve($metric);
|
||||
if ($vars !== false) {
|
||||
$metrics[$curveName][$metric] = $vars;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($metrics)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$intersectingVariables = [];
|
||||
foreach ($metrics as $curveName1 => $_) {
|
||||
foreach ($metrics as $curveName2 => $_) {
|
||||
if ($curveName1 !== $curveName2 && ! isset($intersectingVariables[$curveName2][$curveName1])) {
|
||||
$vars = array_intersect(
|
||||
$this->curves[$curveName1][0]->getMacros(),
|
||||
$this->curves[$curveName2][0]->getMacros()
|
||||
);
|
||||
if (! empty($vars)) {
|
||||
$intersectingVariables[$curveName1][$curveName2] = $vars;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$iterState = [];
|
||||
foreach ($metrics as $curveName => $metric) {
|
||||
$iterState[$curveName] = [0, array_keys($metric)];
|
||||
}
|
||||
|
||||
$metricsCombinations = [];
|
||||
$currentMetrics = [];
|
||||
do {
|
||||
foreach ($metrics as $curveName => $metric) {
|
||||
$currentMetrics[$curveName] = $iterState[$curveName][1][ $iterState[$curveName][0] ];
|
||||
}
|
||||
|
||||
$acceptCombination = true;
|
||||
foreach ($intersectingVariables as $curveName1 => $intersectingWith) {
|
||||
foreach ($intersectingWith as $curveName2 => $vars) {
|
||||
foreach ($vars as $key) {
|
||||
if ($metrics[$curveName1][ $currentMetrics[$curveName1] ][$key]
|
||||
!== $metrics[$curveName2][ $currentMetrics[$curveName2] ][$key]) {
|
||||
$acceptCombination = false;
|
||||
break 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($acceptCombination) {
|
||||
$metricsCombinations[] = $currentMetrics;
|
||||
}
|
||||
|
||||
$overflow = true;
|
||||
foreach ($iterState as $curveName => & $iterSubState) {
|
||||
if (isset($iterSubState[1][ ++$iterSubState[0] ])) {
|
||||
$overflow = false;
|
||||
break;
|
||||
} else {
|
||||
$iterSubState[0] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unset($iterSubState);
|
||||
} while (! $overflow);
|
||||
|
||||
$charts = [];
|
||||
foreach ($metricsCombinations as $metricsCombination) {
|
||||
$charts[] = new Chart($dataSource->getClient(), $this, $metricsCombination);
|
||||
}
|
||||
|
||||
return $charts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get curves to show in a chart by name with Graphite Web metric filters and Graphite functions
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue