diff options
Diffstat (limited to 'library/Graphite/Graphing/MetricsQuery.php')
-rw-r--r-- | library/Graphite/Graphing/MetricsQuery.php | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/library/Graphite/Graphing/MetricsQuery.php b/library/Graphite/Graphing/MetricsQuery.php new file mode 100644 index 0000000..da05c17 --- /dev/null +++ b/library/Graphite/Graphing/MetricsQuery.php @@ -0,0 +1,219 @@ +<?php + +namespace Icinga\Module\Graphite\Graphing; + +use Icinga\Data\Fetchable; +use Icinga\Data\Filter\Filter; +use Icinga\Data\Filterable; +use Icinga\Data\Queryable; +use Icinga\Exception\NotImplementedError; +use Icinga\Module\Graphite\GraphiteUtil; +use Icinga\Module\Graphite\Util\IcingadbUtils; +use Icinga\Module\Graphite\Util\MacroTemplate; +use Icinga\Module\Graphite\Util\InternalProcessTracker as IPT; +use Icinga\Module\Icingadb\Compat\UrlMigrator; +use Icinga\Module\Icingadb\Model\Host; +use Icinga\Module\Monitoring\Object\Macro; +use Icinga\Module\Monitoring\Object\MonitoredObject; +use Icinga\Util\Json; +use Icinga\Web\Url; +use InvalidArgumentException; +use ipl\Orm\Model; +use ipl\Stdlib\Filter as IplFilter; + +/** + * Queries a {@link MetricsDataSource} + */ +class MetricsQuery implements Queryable, Filterable, Fetchable +{ + /** + * @var MetricsDataSource + */ + protected $dataSource; + + /** + * The base metrics pattern + * + * @var MacroTemplate + */ + protected $base; + + /** + * Extension of {@link base} + * + * @var string[] + */ + protected $filter = []; + + /** + * The object to render the graphs for + * + * @var MonitoredObject|Model + */ + protected $object; + + /** + * Constructor + * + * @param MetricsDataSource $dataSource + */ + public function __construct(MetricsDataSource $dataSource) + { + $this->dataSource = $dataSource; + } + + public function from($target, array $fields = null) + { + if ($fields !== null) { + throw new InvalidArgumentException('Fields are not applicable to this kind of query'); + } + + try { + $this->base = $target instanceof MacroTemplate ? $target : new MacroTemplate((string) $target); + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException('Bad target', $e); + } + + return $this; + } + + public function applyFilter(Filter $filter) + { + throw new NotImplementedError(__METHOD__); + } + + public function setFilter(Filter $filter) + { + throw new NotImplementedError(__METHOD__); + } + + public function getFilter() + { + throw new NotImplementedError(__METHOD__); + } + + public function addFilter(Filter $filter) + { + throw new NotImplementedError(__METHOD__); + } + + public function where($condition, $value = null) + { + $this->filter[$condition] = $this->escapeMetricStep($value); + + return $this; + } + + public function fetchAll() + { + $result = []; + foreach ($this->fetchColumn() as $metric) { + $result[] = (object) ['metric' => $metric]; + } + + return $result; + } + + public function fetchRow() + { + $result = $this->fetchColumn(); + return empty($result) ? false : (object) ['metric' => $result[0]]; + } + + public function fetchColumn() + { + $filter = []; + foreach ($this->base->getMacros() as $macro) { + if (isset($this->filter[$macro])) { + $filter[$macro] = $this->filter[$macro]; + continue; + } + + if (strpos($macro, '.') === false) { + continue; + } + + $workaroundMacro = str_replace('.', '_', $macro); + if ($this->object instanceof Model) { + // icingadb macro + $tranformFilter = UrlMigrator::transformFilter( + IplFilter::equal($workaroundMacro, ''), + $this->object instanceof Host ? 'hosts' : 'services' + ); + + if ($tranformFilter === false) { + continue; + } + + $migratedMacro = $tranformFilter->getColumn(); + + if ($migratedMacro === $workaroundMacro) { + $workaroundMacro = $macro; + } else { + $workaroundMacro = $migratedMacro; + } + + $icingadbMacros = IcingadbUtils::getInstance(); + $result = $icingadbMacros->resolveMacro($workaroundMacro, $this->object); + } else { + if ($workaroundMacro === 'service_name') { + $workaroundMacro = 'service_description'; + } + + $result = Macro::resolveMacro($workaroundMacro, $this->object); + } + + if ($result !== $workaroundMacro) { + $filter[$macro] = $this->escapeMetricStep($result); + } + } + + $client = $this->dataSource->getClient(); + $url = Url::fromPath('metrics/expand', [ + 'query' => $this->base->resolve($filter, '*') + ]); + $res = Json::decode($client->request($url)); + natsort($res->results); + + IPT::recordf('Fetched %s metric(s) from %s', count($res->results), (string) $client->completeUrl($url)); + + return array_values($res->results); + } + + public function fetchOne() + { + $result = $this->fetchColumn(); + return empty($result) ? false : $result[0]; + } + + public function fetchPairs() + { + throw new NotImplementedError(__METHOD__); + } + + /** + * Set the object to render the graphs for + * + * @param MonitoredObject|Model $object + * + * @return $this + */ + public function setObject($object) + { + $this->object = $object; + + return $this; + } + + /** + * Escapes a string for usage in a Graphite metric path between two dots + * + * @param string $step + * + * @return string + */ + protected function escapeMetricStep($step) + { + return preg_replace('/[^a-zA-Z0-9\*\-:^[\]$#%\']/', '_', $step); + } +} |