summaryrefslogtreecommitdiffstats
path: root/library/Graphite/Graphing/MetricsQuery.php
diff options
context:
space:
mode:
Diffstat (limited to 'library/Graphite/Graphing/MetricsQuery.php')
-rw-r--r--library/Graphite/Graphing/MetricsQuery.php219
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);
+ }
+}