summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Kunz <david.kunz@progress-linux.org>2016-10-04 11:32:16 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2016-10-04 11:32:22 +0000
commit33310da024fe228b59d41192a629292b3a9995d9 (patch)
tree38c32651d3280dcae1380f9382ed201dc30c3975
parentInitial commit. (diff)
downloadicingaweb2-module-graphite-33310da024fe228b59d41192a629292b3a9995d9.zip
icingaweb2-module-graphite-33310da024fe228b59d41192a629292b3a9995d9.tar.xz
Adding upstream version 0.0.0.5+20160908.upstream/0.0.0.5+20160908upstream
Signed-off-by: David Kunz <david.kunz@progress-linux.org> Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
-rw-r--r--CHANGELOG.md18
-rw-r--r--Capture.PNGbin0 -> 90343 bytes
-rw-r--r--LICENCE21
-rw-r--r--README.md138
-rw-r--r--application/controllers/ConfigController.php24
-rw-r--r--application/controllers/IndexController.php13
-rw-r--r--application/views/scripts/config/index.phtml30
-rw-r--r--application/views/scripts/index/index.phtml17
-rw-r--r--configuration.php7
-rw-r--r--library/Graphite/Grapher.php194
-rw-r--r--library/Graphite/Macro.php121
-rw-r--r--module.info5
-rw-r--r--run.php4
13 files changed, 592 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..2f91408
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,18 @@
+0.0.0.5
+=========
+* Added labels for metrics
+* Added graphite area overload option
+
+0.0.0.4
+=========
+* Removed metric_prefix from config
+* Added metric prefix to macro templates
+* Added metric to macro templates
+
+0.0.0.3
+=========
+* Removed .value from Grapher.php and added to the macro templates, the default templates respect this change.
+
+0.0.0.2
+=========
+* Changed defaults for service_name_template and host_name_template to align with the example configuration.
diff --git a/Capture.PNG b/Capture.PNG
new file mode 100644
index 0000000..3bbc336
--- /dev/null
+++ b/Capture.PNG
Binary files differ
diff --git a/LICENCE b/LICENCE
new file mode 100644
index 0000000..bce6b9b
--- /dev/null
+++ b/LICENCE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 Findmypast
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..79c4e33
--- /dev/null
+++ b/README.md
@@ -0,0 +1,138 @@
+# Graphite module for Icinga Web 2
+
+## General Information
+
+Enable the graphite carbon cache writer: http://docs.icinga.org/icinga2/latest/doc/module/icinga2/chapter/monitoring-basics#graphite-carbon-cache-writer
+
+All perfdata metrics will be automatically included as graphs however if you just want a subset, the host or service then needs to have custom vars of the form vars.graphite_keys =["key1","key2"] where key1 key2 represent perfdata stats you want to see.
+
+You can configure the graphite metric keys formats by using standard-ish icinga2 macros.
+
+## Installation
+
+Just extract this to your Icinga Web 2 module folder in a folder called graphite.
+
+(Configuration -> Modules -> graphite -> enable). Check the modules config tab right there.
+
+NB: It is best practice to install 3rd party modules into a distinct module
+folder like /usr/share/icingaweb2/modules. In case you don't know where this
+might be please check the module path in your Icinga Web 2 configuration.
+
+### Setting up carbon-cache for icinga2
+For graphs to show up correctly in Icinga Web 2, the storage schema for icinga2 needs to be added on the host running Graphite. Usually this configuration file can be found in `/etc/carbon/` or `/opt/graphite/conf/`, depending on how your Graphite server was installed.
+
+Edit the file `storage-schemas.conf` and add the following configuration example for icinga2:
+
+```
+[icinga2_internals]
+pattern = ^icinga2\..*\.(max_check_attempts|reachable|current_attempt|execution_time|latency|state|state_type)
+retentions = 5m:7d
+
+[icinga2_default]
+pattern = ^icinga2\.
+retentions = 5m:10d,30m:90d,360m:4y
+```
+
+**Hint**: Make sure to add these lines before any wild card directives (eg. `pattern = .*`), otherwise they will never match!
+
+Refer to [Configuring Carbon](http://graphite.readthedocs.io/en/latest/config-carbon.html?highlight=storage) for more details.
+
+##Configuration
+There are various configuration settings to tweak how the module behaves and ensure that it aligns with how the graphite carbon cache writer is set up:
+
+``base_url``
+A fully formed url
+* *http://graphite.com/render?*
+
+``legacy_mode``
+To support older versions of the writer pre-icinga2 2.4 where true - is replaced with _
+* *false*
+
+``service_name_template``
+Macro template for the service name
+* *icinga2.$host.name$.services.$service.name$.$service.check_command$.perfdata.$metric$.value*
+
+``host_name_template``
+Macro template for the host name
+* *icinga2.$host.name$.host.$host.check_command$.perfdata.$metric$.value*
+
+``graphite_args_template``
+Macro template for the small image where $target$ is replaced with the metric name
+* *&target=$target$&source=0&width=300&height=120&hideAxes=true&lineWidth=2&hideLegend=true&colorList=049BAF*
+
+``graphite_large_args_template``
+Macro template for the large image
+* *&target=$target$&source=0&width=800&height=700&colorList=049BAF&lineMode=connected*
+
+## Customizing
+As mentioned in General Information above, you can use vars.graphite_keys to limit [or define] the graphs you want to see.
+
+### Limiting graphs
+In our example there are 6 metrics available in perfdata from the check_tomcat script:
+* used
+* free
+* max
+* currentThreadCount
+* maxThreads
+* currentThreadsBusy
+
+To just see the basic graphs of used heap and currentThreadCount, add the following to your Service definition.
+
+ vars.graphite_keys = ["used", "currentThreadCount"]
+
+### Composite Graphs
+In some cases, composite graphs are more useful. Used Heap doesn't make sense unless you know the Max Heap size. To create some useful composite graphs, add the following to your Service definition.
+
+ vars.graphite_keys = ["{used,max}", "{currentThreadsBusy,currentThreadCount}"]
+
+### Graph Labels
+Composite graph definitions can get long and cumbersome, so you can define Labels for each graph. To make short, user-friendly labels for the composite graphs, add the following to your Service definition.
+
+ vars.graphite_keys = ["{used,max}", "{currentThreadsBusy,currentThreadCount}"]
+ vars.graphite_labels = ["Heap", "Threads"]
+
+### Area mode
+It is possible to fill the area below the graphed lines by adding the following to your Service definition.
+
+ vars.graphite.area_mode = "all" // default : "all"
+ vars.graphite.area_alpha = "0.1" // default : "0.1"
+
+To set it globally, customize the configuration file with :
+
+ graphite_area_mode = all
+ graphite_area_alpha = 0.1
+
+For details about the values, see doc here : http://graphite.readthedocs.io/en/latest/render_api.html?highlight=areaMode
+
+### Derivative graphs
+It is possible to plot derivative graphs instead of the measured values. This is useful for sensors giving increasing counters (like printer's page count, network IO bytes counter, ...). You can set it by adding the following to your Service definition.
+
+ vars.graphite.graph_type = "derivative" // default : "normal"
+
+You can customize it's output by setting the summarize interval and summarize function :
+
+ vars.graphite.summarize_interval = "60min" // default : "10min"
+ vars.graphite.summarize_func = "sum" // default : "sum"
+
+To set it globally, customize the configuration file with :
+
+ graphite_summarize_interval = 10min
+
+### Color list
+It is possible to define which colors to use in the graph. You can set it by adding the following to your Service definition.
+
+ vars.graphite.color_list = "2D7DB3,EE1D00" // default : "049BAF,EE1D00,04B06E,0446B0,871E10,CB315D,B06904,B0049C"
+
+To set it globally, customize the configuration file with :
+
+ graphite_color_list = 2D7DB3,EE1D00
+
+This might be specialy useful in conjunction with `vars.graphite_keys` which plots several graphs in the same picture.
+
+## Hats off to
+
+This module borrows a lot from https://github.com/Icinga/icingaweb2-module-pnp4nagios
+
+## What it looks like
+
+![screen shot of graphite graph](https://raw.githubusercontent.com/philiphoy/icingaweb2-module-graphite/master/Capture.PNG)
diff --git a/application/controllers/ConfigController.php b/application/controllers/ConfigController.php
new file mode 100644
index 0000000..cfa6323
--- /dev/null
+++ b/application/controllers/ConfigController.php
@@ -0,0 +1,24 @@
+<?php
+
+use Icinga\Web\Controller\ModuleActionController;
+
+class Graphite_ConfigController extends ModuleActionController
+{
+ public function indexAction()
+ {
+ $this->view->tabs = $this->Module()->getConfigTabs()->activate('config');
+ $hintHtml = $this->view->escape($this->translate(
+ ' In case your graphite web url differs from %s'
+ . ' or if your graphite prefix differs from "%s" please'
+ . ' create a config file'
+ . ' in %s following this example:'
+ ));
+
+ $this->view->escapedHint = sprintf(
+ $hintHtml,
+ '<b>http://graphite.com/render/?</b>',
+ '<b>icinga</b>',
+ '<b>' . $this->Config()->getConfigFile() . '</b>'
+ );
+ }
+}
diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php
new file mode 100644
index 0000000..a9232f2
--- /dev/null
+++ b/application/controllers/IndexController.php
@@ -0,0 +1,13 @@
+<?php
+
+use Icinga\Web\Controller\ActionController;
+
+class Graphite_IndexController extends ActionController
+{
+ public function indexAction()
+ {
+ $this->view->url = $this->getParam('graphite_url');
+ $this->view->iframe_w = $this->getParam('graphite_iframe_w');
+ $this->view->iframe_h = $this->getParam('graphite_iframe_h');
+ }
+}
diff --git a/application/views/scripts/config/index.phtml b/application/views/scripts/config/index.phtml
new file mode 100644
index 0000000..250aea3
--- /dev/null
+++ b/application/views/scripts/config/index.phtml
@@ -0,0 +1,30 @@
+<div class="controls">
+<?= $this->tabs ?>
+</div>
+
+<div class="content">
+<?= $this->form ?>
+<p><?= $this->escapedHint ?></p>
+<pre>
+[graphite]
+metric_prefix = icinga2
+base_url = http://graphite.com/render?
+legacy_mode = false
+;if legacy mode is false (2.4 and newer):
+service_name_template = "icinga2.$host.name$.services.$service.name$.$service.check_command$.perfdata.$metric$.value"
+host_name_template = "icinga2.$host.name$.host.$host.check_command$.perfdata.$metric$.value"
+;if legacy mode is true (pre 2.4):
+;service_name_template = "icinga.$host.name$.services.$service.name$.$service.check_command$"
+;host_name_template = "icinga.$host.name$.host.$host.check_command$"
+;this template is used for the small image, macros $target$ , $areaMode$ and $areaAlpha$ can used.
+graphite_args_template = "&target=$target$&source=0&width=300&height=120&hideAxes=true&lineWidth=2&hideLegend=true&colorList=$colorList$&areaMode=$areaMode$&areaAlpha=$areaAlpha$"
+;this template is used for the large image, macros $target$ , $areaMode$ and $areaAlpha$ can used.
+;graphite_large_args_template = "&target=alias(color($target$_warn,'yellow'),'warning')&target=alias(color($target$_crit,'red'),'critical')&target=$target$&source=0&width=800&height=700&colorList=$colorList$&lineMode=connected&areaMode=$areaMode$&areaAlpha=$areaAlpha$"
+graphite_iframe_w = 800px
+graphite_iframe_h = 700px
+graphite_area_mode = all
+graphite_area_alpha = 0.1
+graphite_summarize_interval = 10min
+graphite_color_list = 049BAF,EE1D00,04B06E,0446B0,871E10,CB315D,B06904,B0049C
+</pre>
+</div>
diff --git a/application/views/scripts/index/index.phtml b/application/views/scripts/index/index.phtml
new file mode 100644
index 0000000..6b91e82
--- /dev/null
+++ b/application/views/scripts/index/index.phtml
@@ -0,0 +1,17 @@
+<div class="controls">
+ <?= $tabs->showOnlyCloseButton() ?>
+</div>
+<div class="content">
+ <table class="avp" style="width: 800px; text-align: center;">
+ <tr>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-10min">10 minutes</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-1h">1 hour</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-12h" active>12 hours</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-1d">1 day</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-1w">1 week</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-1mon">1 month</a></td>
+ <td><a target="graph_iframe" class="icon-clock" href="<?= urldecode($this->url) ?>&from=-1y">1 year</a></td>
+ </tr>
+ </table>
+ <iframe name ="graph_iframe" src="<?= urldecode($this->url) ?>&from=-1d" style="height: <?= $this->iframe_h ?>; width: <?= $this->iframe_w ?>" frameborder="no" scrolling="no" marginwidth="0" marginheight="0" hspace="0" vspace="0"></iframe>
+</div>
diff --git a/configuration.php b/configuration.php
new file mode 100644
index 0000000..409f603
--- /dev/null
+++ b/configuration.php
@@ -0,0 +1,7 @@
+<?php
+
+$this->provideConfigTab('config', array(
+ 'title' => 'Configuration',
+ 'url' => 'config'
+));
+
diff --git a/library/Graphite/Grapher.php b/library/Graphite/Grapher.php
new file mode 100644
index 0000000..e949d3d
--- /dev/null
+++ b/library/Graphite/Grapher.php
@@ -0,0 +1,194 @@
+<?php
+
+namespace Icinga\Module\Graphite;
+
+use Icinga\Application\Config;
+use Icinga\Exception\ConfigurationError;
+use Icinga\Module\Monitoring\Object\MonitoredObject;
+use Icinga\Module\Monitoring\Object\Host;
+use Icinga\Module\Monitoring\Object\Service;
+use Icinga\Module\Monitoring\Plugin\PerfdataSet;
+use Icinga\Web\Hook\GrapherHook;
+use Icinga\Web\Url;
+
+class Grapher extends GrapherHook
+{
+ protected $hasPreviews = true;
+ protected $hasTinyPreviews = true;
+ protected $graphiteConfig;
+ protected $baseUrl = 'http://graphite.com/render/?';
+ protected $serviceMacro = 'icinga2.$host.name$.services.$service.name$.$service.check_command$.perfdata.$metric$.value';
+ protected $hostMacro = 'icinga2.$host.name$.host.$host.check_command$.perfdata.$metric$.value';
+ protected $imageUrlMacro = '&target=$target$&source=0&width=300&height=120&hideAxes=true&lineWidth=2&hideLegend=true&colorList=$colorList$&areaMode=$areaMode$&areaAlpha=$areaAlpha$';
+ protected $largeImageUrlMacro = '&target=$target$&source=0&width=800&height=700&colorList=$colorList$&lineMode=connected&areaMode=$areaMode$&areaAlpha=$areaAlpha$';
+ protected $DerivativeMacro = 'summarize(nonNegativeDerivative($target$),\'$summarizeInterval$\', \'$summarizeFunc$\')';
+ protected $legacyMode = false;
+ protected $graphiteKeys = array();
+ protected $graphiteLabels = array();
+ protected $areaMode = "all";
+ protected $graphType = "normal";
+ protected $summarizeInterval = "10min";
+ protected $summarizeFunc = "sum";
+ protected $areaAlpha = "0.1";
+ protected $colorList = "049BAF,EE1D00,04B06E,0446B0,871E10,CB315D,B06904,B0049C";
+ protected $iframeWidth = "800px";
+ protected $iframeHeight = "700px";
+
+ protected function init()
+ {
+ $cfg = Config::module('graphite')->getSection('graphite');
+ $this->baseUrl = rtrim($cfg->get('base_url', $this->baseUrl), '/');
+ $this->legacyMode = filter_var($cfg->get('legacy_mode', $this->legacyMode), FILTER_VALIDATE_BOOLEAN);
+ $this->serviceMacro = $cfg->get('service_name_template', $this->serviceMacro);
+ $this->hostMacro = $cfg->get('host_name_template', $this->hostMacro);
+ $this->imageUrlMacro = $cfg->get('graphite_args_template', $this->imageUrlMacro);
+ $this->largeImageUrlMacro = $cfg->get('graphite_large_args_template', $this->largeImageUrlMacro);
+ $this->iframeWidth = $cfg->get('graphite_iframe_w', $this->iframeWidth);
+ $this->iframeHeight = $cfg->get('graphite_iframe_h', $this->iframeHeight);
+ $this->areaMode = $cfg->get('graphite_area_mode', $this->areaMode);
+ $this->areaAlpha = $cfg->get('graphite_area_alpha', $this->areaAlpha);
+ $this->summarizeInterval = $cfg->get('graphite_summarize_interval', $this->summarizeInterval);
+ $this->colorList = $cfg->get('graphite_color_list', $this->colorList);
+ }
+
+ private function parseGrapherConfig($graphite_vars)
+ {
+ if (!empty($graphite_vars)) {
+ if (!empty($graphite_vars->area_mode)) {
+ $this->areaMode = $graphite_vars->area_mode;
+ }
+ if (!empty($graphite_vars->area_alpha)) {
+ $this->areaAlpha = $graphite_vars->area_alpha;
+ }
+ if (!empty($graphite_vars->graph_type)) {
+ $this->graphType = $graphite_vars->graph_type;
+ }
+ if (!empty($graphite_vars->summarize_interval)) {
+ $this->summarizeInterval = $graphite_vars->summarize_interval;
+ }
+ if (!empty($graphite_vars->summarize_func)) {
+ $this->summarizeFunc = $graphite_vars->summarize_func;
+ }
+ if (!empty($graphite_vars->color_list)) {
+ $this->colorList = $graphite_vars->color_list;
+ }
+ }
+ }
+
+ private function getKeysAndLabels($vars)
+ {
+ if (array_key_exists("graphite_keys", $vars)) {
+ $this->graphiteKeys = $vars["graphite_keys"];
+ $this->graphiteLabels = $vars["graphite_keys"];
+ if (array_key_exists("graphite_labels", $vars)) {
+ if (count($vars["graphite_keys"]) == count($vars["graphite_labels"])) {
+ $this->graphiteLabels = $vars["graphite_labels"];
+ }
+ }
+ }
+ }
+
+ private function getPerfdataKeys($object)
+ {
+ foreach (PerfdataSet::fromString($object->perfdata)->asArray() as $pd) {
+ $this->graphiteKeys[] = $pd->getLabel();
+ $this->graphiteLabels[] = $pd->getLabel();
+ }
+ }
+
+ private function getPreviewImage($host, $service, $metric)
+ {
+ if ($host != null){
+ $target = Macro::resolveMacros($this->hostMacro, $host, $this->legacyMode, true);
+ } elseif ($service != null ){
+ $target = Macro::resolveMacros($this->serviceMacro, $service, $this->legacyMode, true);
+ } else {
+ $target = '';
+ }
+
+ if ($this->graphType == "derivative"){
+ $target = Macro::resolveMacros($this->DerivativeMacro, array(
+ "target" => $target,
+ "summarizeInterval" => $this->summarizeInterval,
+ "summarizeFunc" => $this->summarizeFunc
+ ), $this->legacyMode, false, false);
+ }
+ $target = Macro::resolveMacros($target, array("metric"=>$metric), $this->legacyMode, true, true);
+ $imgUrl = $this->baseUrl . Macro::resolveMacros($this->imageUrlMacro, array(
+ "target" => $target,
+ "areaMode" => $this->areaMode,
+ "areaAlpha" => $this->areaAlpha,
+ "colorList" => $this->colorList
+ ), $this->legacyMode);
+ $largeImgUrl = $this->baseUrl . Macro::resolveMacros($this->largeImageUrlMacro, array(
+ "target" => $target,
+ "areaMode" => $this->areaMode,
+ "areaAlpha" => $this->areaAlpha,
+ "colorList" => $this->colorList
+ ), $this->legacyMode);
+
+ $url = Url::fromPath('graphite', array(
+ 'graphite_url' => urlencode($largeImgUrl),
+ 'graphite_iframe_w' => urlencode($this->iframeWidth),
+ 'graphite_iframe_h' => urlencode($this->iframeHeight)
+ ));
+
+ $html = '<a href="%s" title="%s"><img src="%s" alt="%s" width="300" height="120" /></a>';
+
+ return sprintf(
+ $html,
+ $url,
+ $metric,
+ $imgUrl,
+ $metric
+ );
+ }
+
+ public function has(MonitoredObject $object)
+ {
+ if (($object instanceof Host)||($object instanceof Service)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getPreviewHtml(MonitoredObject $object)
+ {
+ $object->fetchCustomvars();
+
+ if (array_key_exists("graphite", $object->customvars)) {
+ $this->parseGrapherConfig($object->customvars["graphite"]);
+ }
+
+ $this->getKeysAndLabels($object->customvars);
+ if (empty($this->graphiteKeys)) {
+ $this->getPerfDataKeys($object);
+ }
+
+ if ($object instanceof Host) {
+ $host = $object;
+ $service = null;
+ } elseif ($object instanceof Service) {
+ $service = $object;
+ $host = null;
+ } else {
+ return '';
+ }
+
+ $html = "<table class=\"avp newsection\">\n"
+ ."<tbody>\n";
+
+ for ($key = 0; $key < count($this->graphiteKeys); $key++) {
+ $html .= "<tr><th>\n"
+ . $this->graphiteLabels[$key]
+ . '</th><td>'
+ . $this->getPreviewImage($host, $service, $this->graphiteKeys[$key])
+ . "</td>\n"
+ . "<tr>\n";
+ }
+
+ $html .= "</tbody></table>\n";
+ return $html;
+ }
+}
diff --git a/library/Graphite/Macro.php b/library/Graphite/Macro.php
new file mode 100644
index 0000000..b056951
--- /dev/null
+++ b/library/Graphite/Macro.php
@@ -0,0 +1,121 @@
+<?php
+/* Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
+
+namespace Icinga\Module\Graphite;
+
+/**
+ * Expand macros in string in the context of MonitoredObjects
+ */
+class Macro
+{
+ /**
+ * Known icinga macros
+ *
+ * @var array
+ */
+ private static $icingaMacros = array(
+ 'HOSTNAME' => 'host_name',
+ 'HOSTADDRESS' => 'host_address',
+ 'SERVICEDESC' => 'service_description',
+ 'service.name' => 'service_description'
+ );
+
+ /**
+ * Return the given string with macros being resolved
+ *
+ * @param string $input The string in which to look for macros
+ * @param MonitoredObject|stdClass $object The host or service used to resolve macros
+ *
+ * @return string The substituted or unchanged string
+ */
+ public static function resolveMacros($input, $object, $legacyMode, $escape = false , $isMacro = false)
+ {
+ $matches = array();
+ if (preg_match_all('@\$([^\$\s]+)\$@', $input, $matches)) {
+ foreach ($matches[1] as $key => $value) {
+ $newValue = self::resolveMacro($value, $object);
+ if ($newValue !== $value) {
+ if ($escape){
+ $newValue = self::escapeMetric($newValue, $legacyMode, $isMacro);
+ }
+ $input = str_replace($matches[0][$key], $newValue, $input);
+ }
+ }
+ }
+
+ return $input;
+ }
+
+
+ /**
+ * Resolve a macro based on the given object
+ *
+ * @param string $macro The macro to resolve
+ * @param MonitoredObject|stdClass $object The object used to resolve the macro
+ *
+ * @return string The new value or the macro if it cannot be resolved
+ */
+ public static function resolveMacro($macro, $object)
+ {
+ if (is_array($object) && array_key_exists($macro, $object)) {
+ return $object[$macro];
+ }
+
+ if (array_key_exists($macro, self::$icingaMacros) && $object->{self::$icingaMacros[$macro]} !== false) {
+ return $object->{self::$icingaMacros[$macro]};
+ }
+
+ if (array_key_exists($macro, $object->customvars)) {
+ return $object->customvars[$macro];
+ }
+
+ $translated = self::translateTerm($macro);
+
+ if ($translated !== NULL) {
+ if (array_key_exists($translated, $object->customvars)) {
+ return $object->customvars[$translated];
+ }
+
+ if ($object->{$translated} !== false) {
+ return $object->{$translated};
+ }
+ }
+
+ return $macro;
+ }
+
+ public static function escapeMetric($str, $legacyMode, $ismetric)
+ {
+ if ($legacyMode) {
+ $str=str_replace('-','_',$str);
+ $str=str_replace('.','_',$str);
+ } elseif (!$ismetric){
+ $str=str_replace('.','_',$str);
+ }
+
+ $str=str_replace(' ','_',$str);
+ $str=str_replace('\\','_',$str);
+ $str=str_replace('/','_',$str);
+ $str=str_replace('::','.',$str);
+ return $str;
+ }
+
+ private static function translateTerm($term){
+
+ if (substr($term, 0, strlen('host.vars.')) === 'host.vars.'){
+ return str_replace('host.vars.','',$term);
+ }
+
+ if (substr($term, 0, strlen('service.vars.')) === 'service.vars.'){
+ return str_replace('service.vars.','',$term);
+ }
+
+ if (substr($term, 0, strlen('service.')) === 'service.'){
+ return str_replace('service.','service_',$term);
+ }
+
+ if (substr($term, 0, strlen('host.')) === 'host.'){
+ return str_replace('host.','host_',$term);
+ }
+ }
+}
diff --git a/module.info b/module.info
new file mode 100644
index 0000000..7b9c190
--- /dev/null
+++ b/module.info
@@ -0,0 +1,5 @@
+Module: graphite
+Version: 0.0.0.5
+Dependencies: Icinga2 graphite carbon cache writer
+Description: Graphite graphs for perfdata
+ Shows graphite graphs for captured metrics. The monitored service needs to have custom vars of the form vars.graphite_keys =["key1","key2"]
diff --git a/run.php b/run.php
new file mode 100644
index 0000000..c0dd4af
--- /dev/null
+++ b/run.php
@@ -0,0 +1,4 @@
+<?php
+
+$this->registerHook('grapher', '\\Icinga\\Module\\Graphite\\Grapher');
+