diff options
Diffstat (limited to '')
-rw-r--r-- | library/Graphite/Util/IcingadbUtils.php | 49 | ||||
-rw-r--r-- | library/Graphite/Util/InternalProcessTracker.php | 126 | ||||
-rw-r--r-- | library/Graphite/Util/MacroTemplate.php | 239 | ||||
-rw-r--r-- | library/Graphite/Util/TimeRangePickerTools.php | 111 |
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]; + } +} |