summaryrefslogtreecommitdiffstats
path: root/library/Graphite/Util
diff options
context:
space:
mode:
Diffstat (limited to 'library/Graphite/Util')
-rw-r--r--library/Graphite/Util/IcingadbUtils.php49
-rw-r--r--library/Graphite/Util/InternalProcessTracker.php126
-rw-r--r--library/Graphite/Util/MacroTemplate.php239
-rw-r--r--library/Graphite/Util/TimeRangePickerTools.php111
4 files changed, 525 insertions, 0 deletions
diff --git a/library/Graphite/Util/IcingadbUtils.php b/library/Graphite/Util/IcingadbUtils.php
new file mode 100644
index 0000000..43334e5
--- /dev/null
+++ b/library/Graphite/Util/IcingadbUtils.php
@@ -0,0 +1,49 @@
+<?php
+
+/* Icinga Graphite Web | (c) 2022 Icinga GmbH | GPLv2 */
+
+namespace Icinga\Module\Graphite\Util;
+
+use Icinga\Module\Icingadb\Common\Auth;
+use Icinga\Module\Icingadb\Common\Database;
+use Icinga\Module\Icingadb\Common\Macros;
+
+/**
+* Class for initialising icingadb utils
+ */
+class IcingadbUtils
+{
+ use Macros;
+ use Database;
+ use Auth;
+
+ protected static $instance;
+
+ /**
+ * @see getInstance()
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Get the IcingadbUtils instance
+ *
+ * @return IcingadbUtils
+ */
+ public static function getInstance(): IcingadbUtils
+ {
+ if (self::$instance === null) {
+ self::$instance = new self();
+ }
+
+ return static::$instance;
+ }
+
+ /**
+ * Prevent the instance from being cloned (which would create a second instance of it)
+ */
+ private function __clone()
+ {
+ }
+}
diff --git a/library/Graphite/Util/InternalProcessTracker.php b/library/Graphite/Util/InternalProcessTracker.php
new file mode 100644
index 0000000..f7f2df6
--- /dev/null
+++ b/library/Graphite/Util/InternalProcessTracker.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Icinga\Module\Graphite\Util;
+
+use Icinga\Authentication\Auth;
+use Icinga\Security\SecurityException;
+
+/**
+ * A record about what happened during a specific action
+ */
+class InternalProcessTracker
+{
+ /**
+ * Whether to record anything
+ *
+ * @var bool
+ */
+ private static $enabled = false;
+
+ /**
+ * How many '+'es to prepend to each new record
+ *
+ * @var int
+ */
+ private static $indentation = 1;
+
+ /**
+ * The recorded happenings
+ *
+ * @var string[]
+ */
+ private static $records = [];
+
+ /**
+ * Get whether recording is enabled
+ *
+ * @return bool
+ */
+ public static function enabled()
+ {
+ return self::$enabled;
+ }
+
+ /**
+ * Enable recording
+ *
+ * @throws SecurityException
+ */
+ public static function enable()
+ {
+ if (! Auth::getInstance()->hasPermission('graphite/debug')) {
+ throw new SecurityException('No permission for graphite/debug');
+ }
+
+ self::$enabled = true;
+ }
+
+ /**
+ * Introduce a "sub-process"
+ */
+ public static function indent()
+ {
+ if (self::$enabled) {
+ ++self::$indentation;
+ }
+ }
+
+ /**
+ * Record a happening
+ *
+ * Behaves like {@link sprintf()} if additional arguments given, but {@link var_export()}s the arguments first
+ * (so always use %s instead of e.g. %d).
+ *
+ * @param string $format
+ */
+ public static function recordf($format)
+ {
+ if (self::$enabled) {
+ if (func_num_args() > 1) {
+ $args = [];
+ foreach (array_slice(func_get_args(), 1) as $arg) {
+ $args[] = var_export($arg, true);
+ }
+
+ $format = vsprintf($format, $args);
+ }
+
+ self::$records[] = str_repeat('+', self::$indentation) . " $format";
+ }
+ }
+
+ /**
+ * Terminate a "sub-process"
+ */
+ public static function unindent()
+ {
+ if (self::$enabled) {
+ --self::$indentation;
+ }
+ }
+
+ /**
+ * Dump everything recorded as plain text
+ *
+ * @return string
+ */
+ public static function dump()
+ {
+ return implode("\n", self::$records);
+ }
+
+ /**
+ * Reset records
+ */
+ public static function clear()
+ {
+ if (self::$enabled) {
+ self::$indentation = 1;
+ self::$records = [];
+ }
+ }
+
+ final private function __construct()
+ {
+ }
+}
diff --git a/library/Graphite/Util/MacroTemplate.php b/library/Graphite/Util/MacroTemplate.php
new file mode 100644
index 0000000..23a171a
--- /dev/null
+++ b/library/Graphite/Util/MacroTemplate.php
@@ -0,0 +1,239 @@
+<?php
+
+namespace Icinga\Module\Graphite\Util;
+
+use InvalidArgumentException;
+
+/**
+ * A macro-based template for strings
+ */
+class MacroTemplate
+{
+ /**
+ * Macros' start and end character
+ *
+ * @var string
+ */
+ protected $macroCharacter;
+
+ /**
+ * The parsed template
+ *
+ * @var string[]
+ */
+ protected $template;
+
+ /**
+ * Regex for reverse resolving patterns
+ *
+ * @var string
+ */
+ protected $reverseResolvePattern;
+
+ /**
+ * Wildcards
+ *
+ * @var string[]
+ */
+ protected $wildCards;
+
+ /**
+ * The original raw template
+ *
+ * @var string
+ */
+ protected $orgTemplate;
+
+ /**
+ * Constructor
+ *
+ * @param string $template The raw template
+ * @param string $macroCharacter Macros' start and end character
+ */
+ public function __construct($template, $macroCharacter = '$')
+ {
+ $this->orgTemplate = $template;
+ $this->macroCharacter = $macroCharacter;
+ $this->template = explode($macroCharacter, $template);
+ foreach ($this->template as $key => $value) {
+ if (preg_match('/([^:]+):(.+)/', $value, $match)) {
+ $wildCardKey = $match[1];
+ $this->template[$key] = $wildCardKey;
+ $this->wildCards[$wildCardKey] = $match[2];
+ }
+ }
+
+ if (! (count($this->template) % 2)) {
+ throw new InvalidArgumentException(
+ 'template contains odd number of ' . var_export($macroCharacter, true)
+ . 's: ' . var_export($template, true)
+ );
+ }
+ }
+
+ /**
+ * Return a string based on this template with the macros resolved from the given variables
+ *
+ * @param string[] $variables
+ * @param string $default The default value for missing variables.
+ * By default the macro just isn't replaced.
+ *
+ * @return string
+ */
+ public function resolve(array $variables, $default = null)
+ {
+ $macro = false;
+ $result = []; // kind of string builder
+
+ foreach ($this->template as $part) {
+ if ($macro) {
+ if (isset($variables[$part])) {
+ $result[] = $variables[$part];
+ } elseif ($part === '') {
+ $result[] = $this->macroCharacter;
+ } elseif ($default === null) {
+ $result[] = $this->macroCharacter;
+ $result[] = $part;
+ // add wildcards to result before they are
+ // overwritten from Template::getFullCurves()
+ if (isset($this->wildCards[$part])) {
+ $result[] = ':' . $this->wildCards[$part];
+ }
+
+ $result[] = $this->macroCharacter;
+ } else {
+ if (isset($this->wildCards[$part])) {
+ $result[] = $this->wildCards[$part];
+ } else {
+ $result[] = $default;
+ }
+ }
+ } else {
+ $result[] = $part;
+ }
+
+ $macro = ! $macro;
+ }
+
+ return implode($result);
+ }
+
+ /**
+ * Try to reverse-resolve the given string
+ *
+ * @param string $resolved A result of {@link resolve()}
+ *
+ * @return string[]|false Variables as passed to {@link resolve()} if successful
+ */
+ public function reverseResolve($resolved)
+ {
+ $matches = [];
+ if (! preg_match($this->getReverseResolvePattern(), $resolved, $matches)) {
+ return false;
+ }
+
+ $macro = false;
+ $macros = [];
+ $currentCapturedSubPatternIndex = 0;
+
+ foreach ($this->template as $part) {
+ if ($macro && ! isset($macros[$part])) {
+ $macros[$part] = ++$currentCapturedSubPatternIndex;
+ }
+
+ $macro = ! $macro;
+ }
+
+ $macros = array_flip($macros);
+
+ $result = [];
+ foreach ($matches as $index => $match) {
+ if ($index > 0) {
+ $result[$macros[$index]] = $match;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return the raw template string this instance was constructed from
+ *
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->orgTemplate;
+ }
+
+ /**
+ * Return the macros of this template
+ *
+ * @return string[]
+ */
+ public function getMacros()
+ {
+ $macro = false;
+ $macros = [];
+
+ foreach ($this->template as $part) {
+ if ($macro) {
+ $macros[$part] = null;
+ }
+
+ $macro = ! $macro;
+ }
+
+ unset($macros['']);
+
+ return array_keys($macros);
+ }
+
+ /**
+ * Get macros' start and end character
+ *
+ * @return string
+ */
+ public function getMacroCharacter()
+ {
+ return $this->macroCharacter;
+ }
+
+ /**
+ * Get {@link reverseResolvePattern}
+ *
+ * @return string
+ */
+ protected function getReverseResolvePattern()
+ {
+ if ($this->reverseResolvePattern === null) {
+ $result = ['/\A']; // kind of string builder
+ $macro = false;
+ $macros = [];
+ $currentCapturedSubPatternIndex = 0;
+
+ foreach ($this->template as $part) {
+ if ($macro) {
+ if (isset($macros[$part])) {
+ $result[] = '\g{';
+ $result[] = $macros[$part];
+ $result[] = '}';
+ } else {
+ $macros[$part] = ++$currentCapturedSubPatternIndex;
+ $result[] = '(.*)';
+ }
+ } else {
+ $result[] = preg_quote($part, '/');
+ }
+
+ $macro = ! $macro;
+ }
+
+ $result[] = '\z/s';
+
+ $this->reverseResolvePattern = implode($result);
+ }
+
+ return $this->reverseResolvePattern;
+ }
+}
diff --git a/library/Graphite/Util/TimeRangePickerTools.php b/library/Graphite/Util/TimeRangePickerTools.php
new file mode 100644
index 0000000..d1ebc75
--- /dev/null
+++ b/library/Graphite/Util/TimeRangePickerTools.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Icinga\Module\Graphite\Util;
+
+use Icinga\Application\Config;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Web\Url;
+use Icinga\Web\UrlParams;
+
+final class TimeRangePickerTools
+{
+ /**
+ * @return string
+ */
+ public static function getRelativeRangeParameter()
+ {
+ return 'graph_range';
+ }
+
+ /**
+ * @return string[]
+ */
+ public static function getAbsoluteRangeParameters()
+ {
+ return ['start' => 'graph_start', 'end' => 'graph_end'];
+ }
+
+ /**
+ * @return string[]
+ */
+ public static function getAllRangeParameters()
+ {
+ return array_values(array_merge([static::getRelativeRangeParameter()], static::getAbsoluteRangeParameters()));
+ }
+
+ /**
+ * Copy {@link getAllRangeParameters()} from one {@link UrlParams} instance to another
+ *
+ * @param UrlParams|null $copy Defaults to a new instance
+ * @param UrlParams|null $origin Defaults to the current request's params
+ *
+ * @return UrlParams The copy
+ */
+ public static function copyAllRangeParameters(UrlParams $copy = null, UrlParams $origin = null)
+ {
+ if ($origin === null) {
+ $origin = Url::fromRequest()->getParams();
+ }
+ if ($copy === null) {
+ $copy = new UrlParams();
+ }
+
+ foreach (self::getAllRangeParameters() as $param) {
+ $value = $origin->get($param);
+ if ($value !== null) {
+ $copy->set($param, $value);
+ }
+ }
+
+ return $copy;
+ }
+
+ /**
+ * Extract the relative time range (if any) from the given URL parameters
+ *
+ * @param UrlParams $params
+ *
+ * @return bool|int|null
+ */
+ public static function getRelativeSeconds(UrlParams $params)
+ {
+ $seconds = $params->get(self::getRelativeRangeParameter());
+ if ($seconds === null) {
+ return null;
+ }
+
+ return preg_match('/^(?:0|[1-9]\d*)$/', $seconds) ? (int) $seconds : false;
+ }
+
+ /**
+ * Get the default relative time range for graphs
+ *
+ * @return int
+ *
+ * @throws ConfigurationError
+ */
+ public static function getDefaultRelativeTimeRange()
+ {
+ $rangeFactors = [
+ 'minutes' => 60,
+ 'hours' => 3600,
+ 'days' => 86400,
+ 'weeks' => 604800,
+ 'months' => 2592000,
+ 'years' => 31557600
+ ];
+
+ $config = Config::module('graphite');
+ $unit = $config->get('ui', 'default_time_range_unit', 'hours');
+
+ if (! isset($rangeFactors[$unit])) {
+ throw new ConfigurationError(
+ 'Bad ui.default_time_range_unit %s in file %s',
+ var_export($unit, true),
+ var_export($config->getConfigFile(), true)
+ );
+ }
+
+ return (int) $config->get('ui', 'default_time_range', 1) * $rangeFactors[$unit];
+ }
+}