diff --git a/application/controllers/ShowController.php b/application/controllers/ShowController.php index 427142e..849f8c6 100644 --- a/application/controllers/ShowController.php +++ b/application/controllers/ShowController.php @@ -7,6 +7,7 @@ use Icinga\Exception\NotFoundError; use Icinga\Module\Graphite\GraphiteChart; use Icinga\Module\Graphite\GraphiteUtil; use Icinga\Module\Graphite\GraphiteWeb; +use Icinga\Module\Graphite\GraphiteWebClient; use Icinga\Module\Graphite\GraphTemplate; use Icinga\Module\Graphite\TemplateStore; use Icinga\Web\Controller; @@ -14,8 +15,6 @@ use Icinga\Web\Widget; class ShowController extends Controller { - protected $baseUrl; - protected $graphiteWeb; protected $templates; @@ -34,8 +33,7 @@ class ShowController extends Controller { $config = $this->Config(); $this->templateStore = new TemplateStore(); - $this->baseUrl = $this->Config()->get('graphite', 'web_url'); - $graphite = $this->graphiteWeb = new GraphiteWeb($this->baseUrl); + $graphite = $this->graphiteWeb = new GraphiteWeb(GraphiteWebClient::getInstance()); $this->template = $this->view->template = $this->loadTemplate(); $this->params->shift('r'); } diff --git a/application/forms/ConfigForm.php b/application/forms/ConfigForm.php index ac351df..4c40143 100644 --- a/application/forms/ConfigForm.php +++ b/application/forms/ConfigForm.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Graphite\Forms; use Icinga\Forms\ConfigForm as BaseConfigForm; +use Icinga\Module\Graphite\Web\Form\Validator\HttpUserValidator; class ConfigForm extends BaseConfigForm { @@ -14,15 +15,36 @@ class ConfigForm extends BaseConfigForm public function createElements(array $formData) { - $this->addElement( - 'text', - 'graphite_web_url', - array( - 'required' => true, - 'label' => $this->translate('Graphite Web URL'), - 'description' => $this->translate('URL to your Graphite Web'), - 'validators' => ['UrlValidator'] - ) - ); + $this->addElements([ + [ + 'text', + 'graphite_web_url', + [ + 'required' => true, + 'label' => $this->translate('Graphite Web URL'), + 'description' => $this->translate('URL to your Graphite Web'), + 'validators' => ['UrlValidator'] + ] + ], + [ + 'text', + 'graphite_web_user', + [ + 'label' => $this->translate('Graphite Web user'), + 'description' => $this->translate( + 'A user with access to your Graphite Web via HTTP basic authentication' + ), + 'validators' => [new HttpUserValidator()] + ] + ], + [ + 'password', + 'graphite_web_password', + [ + 'label' => $this->translate('Graphite Web password'), + 'description' => $this->translate('The above user\'s password') + ] + ] + ]); } } diff --git a/library/Graphite/GraphiteChart.php b/library/Graphite/GraphiteChart.php index 4217d13..13d2f1a 100644 --- a/library/Graphite/GraphiteChart.php +++ b/library/Graphite/GraphiteChart.php @@ -95,27 +95,20 @@ class GraphiteChart public function getUrl() { - $urlPattern = '/^' . preg_quote(Url::fromPath('/'), '/') . '/'; $url = Url::fromPath('/render', $this->getParams()); $this->template->extendUrl($url, $this->metric, $this->vars); $url->getParams()->add('_ext', 'whatever.svg'); - $url = preg_replace($urlPattern, $this->web->getBaseUrl() . '/', $url); return $url; } public function fetchImage() { - $options = array( - 'http'=>array( - 'method'=>"POST", - 'header'=> - "Accept-language: en\r\n". - "Content-type: application/x-www-form-urlencoded\r\n", -// 'content'=> $data - ) + $url = $this->getUrl(); + return $this->web->getClient()->request( + $url->getPath(), + $url->getParams(), + 'POST', + ['Accept-language' => 'en', 'Content-type' => 'application/x-www-form-urlencoded'] ); - - $context = stream_context_create($options); - return file_get_contents($this->getUrl(), false, $context); } } diff --git a/library/Graphite/GraphiteWeb.php b/library/Graphite/GraphiteWeb.php index 4f9fb5d..4ba57ac 100644 --- a/library/Graphite/GraphiteWeb.php +++ b/library/Graphite/GraphiteWeb.php @@ -13,20 +13,20 @@ use Icinga\Module\Graphite\GraphiteQuery; class GraphiteWeb { /** - * Graphite webapp base url + * HTTP interface to Graphite Web * - * @var string + * @var GraphiteWebClientInterface */ - protected $baseUrl; + private $client; /** * Construct a new graphite webapp instance * - * @param $baseUrl string Graphite webapp base url + * @param GraphiteWebClientInterface $client HTTP interface to Graphite Web */ - public function __construct($baseUrl) + public function __construct(GraphiteWebClientInterface $client) { - $this->baseUrl = $baseUrl; + $this->client = $client; } /** @@ -40,13 +40,13 @@ class GraphiteWeb } /** - * Retrieve out base url + * Get {@link client} * - * @return string + * @return GraphiteWebClientInterface */ - public function getBaseUrl() + public function getClient() { - return $this->baseUrl; + return $this->client; } /** @@ -56,11 +56,7 @@ class GraphiteWeb */ public function listMetrics($filter) { - $res = json_decode( - file_get_contents( - $this->baseUrl . '/metrics/expand?query=' . $filter - ) - ); + $res = json_decode($this->client->request('metrics/expand', ['query' => $filter])); natsort($res->results); return array_values($res->results); } diff --git a/library/Graphite/GraphiteWebClient.php b/library/Graphite/GraphiteWebClient.php new file mode 100644 index 0000000..52c4eaa --- /dev/null +++ b/library/Graphite/GraphiteWebClient.php @@ -0,0 +1,67 @@ + $method, 'header' => '']; + foreach (array_merge($this->baseHeaders, $headers) as $header => $headerValue) { + $httpOptions['header'] .= "$header: $headerValue\r\n"; + } + + if ($body !== null) { + $httpOptions['content'] = $body; + } + + $url = Url::fromPath($this->baseUrl . ltrim($url, '/')); + if ($params !== null) { + $url->setParams($params); + } + + // TODO(ak): use our CurlClient (one nice day) + return file_get_contents($url->getAbsoluteUrl(), false, stream_context_create(['http' => $httpOptions])); + } + + private function init() + { + $config = Config::module('graphite'); + $graphite = $config->getSection('graphite'); + $baseUrl = $graphite->web_url; + if ($baseUrl === null) { + throw new ConfigurationError('Missing graphite.web_url in "%s"', $config->getConfigFile()); + } + + $this->baseUrl = rtrim($baseUrl, '/') . '/'; + + $this->baseHeaders = ['User-Agent' => 'icingaweb2-module-graphite']; + if (isset($graphite->web_user) && isset($graphite->web_password)) { + $this->baseHeaders['Authorization'] = 'Basic ' . base64_encode( + "{$graphite->web_user}:{$graphite->web_password}" + ); + } + } +} diff --git a/library/Graphite/GraphiteWebClientInterface.php b/library/Graphite/GraphiteWebClientInterface.php new file mode 100644 index 0000000..f2dfc7b --- /dev/null +++ b/library/Graphite/GraphiteWebClientInterface.php @@ -0,0 +1,28 @@ +init(); + } + + /** + * Ensure that no one can clone the object + */ + final private function __clone() + { + throw new Exception('Won\'t clone a singleton'); + } + + /** + * Initializer + * + * May be overridden. + */ + private function init() + { + } +} diff --git a/library/Graphite/Web/Form/Validator/HttpUserValidator.php b/library/Graphite/Web/Form/Validator/HttpUserValidator.php new file mode 100644 index 0000000..d5f4a86 --- /dev/null +++ b/library/Graphite/Web/Form/Validator/HttpUserValidator.php @@ -0,0 +1,30 @@ +_messageTemplates = ['HAS_COLON' => mt('graphite', 'The username must not contain colons.')]; + } + + public function isValid($value) + { + $hasColon = false !== strpos($value, ':'); + if ($hasColon) { + $this->_error('HAS_COLON'); + } + return ! $hasColon; + } +}