From 07afe2131a7f5777a32ebbab40408848874645e9 Mon Sep 17 00:00:00 2001 From: Michael Friedrich Date: Sat, 22 Apr 2017 22:33:03 +0200 Subject: [PATCH] Better error handling for proxy access mode - use curl_set_opts - do not initialize $this->auth with an `@` at the end, but pass this directly to curl_opts (bug introduced in 70a61a3b37965aefcfe70d5df8be25ab43aa2deb) - curl_error($curl_handle) is not a safe error handling when RETURNTRANSFER is set to true (must check for $res === false, http://stackoverflow.com/questions/16452636/php-curl-what-does-curl-exec-return) - fetch the http status code and return an error - add Util class with http status code translation to string - better explanation of `accesscode` in the docs fixes #31 fixes #32 --- README.md | 2 +- library/Grafana/ProvidedHook/Grapher.php | 49 +++++++++++++------- library/Grafana/Util.php | 59 ++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 18 deletions(-) create mode 100644 library/Grafana/Util.php diff --git a/README.md b/README.md index 21995de..076f7f5 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ enableLink | **Optional.** Enable/disable graph with a rendered URL to t datasource | **Required.** Type of the Grafana datasource (`influxdb`, `graphite` or `pnp`). Defaults to `influxdb`. defaultdashboard | **Required.** Name of the default dashboard which will be shown for unconfigured graphs. **Important: `panelID` must be set to `1`!** Defaults to `icinga2-default`. defaultdashboardstore | **Optional.** Grafana backend (file or database). Defaults to `Database`. -accessmode | **Optional.** Controlls if module proxies all graphs or user loads graphs directly. Direct access speeds up page load, needs `auth.anonymous` enabled in Grafana. Defaults to `proxy`. +accessmode | **Optional.** Controls whether graphs are fetched with curl (`proxy`) or are embedded (`direct`). Direct access is faster and needs `auth.anonymous` enabled in Grafana. Defaults to `proxy`. Example: diff --git a/library/Grafana/ProvidedHook/Grapher.php b/library/Grafana/ProvidedHook/Grapher.php index 6f21ef6..cfd5d46 100644 --- a/library/Grafana/ProvidedHook/Grapher.php +++ b/library/Grafana/ProvidedHook/Grapher.php @@ -5,12 +5,14 @@ use Icinga\Application\Icinga; use Icinga\Application\Config; use Icinga\Exception\ConfigurationError; +use Exception; use Icinga\Application\Hook\GrapherHook; use Icinga\Module\Monitoring\Object\MonitoredObject; use Icinga\Module\Monitoring\Object\Host; use Icinga\Module\Monitoring\Object\Service; use Icinga\Web\Url; use Icinga\Web\View; +use Icinga\Module\Grafana\Util; class Grapher extends GrapherHook { @@ -83,12 +85,12 @@ protected function init() { if($this->password != null) { - $this->auth = $this->username.":".$this->password."@"; + $this->auth = $this->username.":".$this->password; } else - { - $this->auth = $this->username."@"; - } + { + $this->auth = $this->username; + } } else { @@ -177,22 +179,35 @@ private function getPreviewImage($serviceName, $hostName) // fetch image with curl $curl_handle = curl_init(); - curl_setopt($curl_handle,CURLOPT_URL,$pngUrl); - curl_setopt($curl_handle,CURLOPT_CONNECTTIMEOUT,2); - curl_setopt($curl_handle,CURLOPT_RETURNTRANSFER,true); - curl_setopt($curl_handle,CURLOPT_SSL_VERIFYPEER,false); - curl_setopt($curl_handle,CURLOPT_TIMEOUT,5); - curl_setopt($curl_handle, CURLOPT_USERPWD, "$this->auth"); - curl_setopt($curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); - $imgBinary = curl_exec($curl_handle); - if(curl_error($curl_handle)) - { - return 'Graph currently unavailable: :' . curl_error($curl_handle); + $curl_opts = array( + CURLOPT_URL => $pngUrl, + CURLOPT_CONNECTTIMEOUT => 2, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, //TODO: config option + CURLOPT_TIMEOUT => 5, //TODO: config option + CURLOPT_USERPWD => "$this->auth", + CURLOPT_HTTPAUTH, CURLAUTH_ANY + ); + + curl_setopt_array($curl_handle, $curl_opts); + + $res = curl_exec($curl_handle); + + if ($res === false) { + return "Graph currently unavailable: Curl error: ' . curl_error($curl_handle)"; } - curl_close($curl_handle); + $statusCode = curl_getinfo($curl_handle, CURLINFO_HTTP_CODE); + + if ($statusCode > 299) { + $error = @json_decode($res); + return "Cannot fetch Grafana graph: ". Util::httpStatusCodeToString($statusCode) . + " ($statusCode): " . (property_exists($error, 'message') ? $error->message : ""); + } + + curl_close($curl_handle); - $img = 'data:image/png;base64,'.base64_encode($imgBinary); + $img = 'data:image/png;base64,'.base64_encode($res); $imghtml = '%s'; return sprintf( $imghtml, diff --git a/library/Grafana/Util.php b/library/Grafana/Util.php new file mode 100644 index 0000000..c4c5778 --- /dev/null +++ b/library/Grafana/Util.php @@ -0,0 +1,59 @@ + 'Continue', + '101' => 'Switching Protocols', + '200' => 'OK', + '201' => 'Created', + '202' => 'Accepted', + '203' => 'Non-Authoritative Information', + '204' => 'No Content', + '205' => 'Reset Content', + '206' => 'Partial Content', + '300' => 'Multiple Choices', + '302' => 'Found', + '303' => 'See Other', + '304' => 'Not Modified', + '305' => 'Use Proxy', + '400' => 'Bad Request', + '401' => 'Unauthorized', + '402' => 'Payment Required', + '403' => 'Forbidden', + '404' => 'Not Found', + '405' => 'Method Not Allowed', + '406' => 'Not Acceptable', + '407' => 'Proxy Authentication Required', + '408' => 'Request Timeout', + '409' => 'Conflict', + '410' => 'Gone', + '411' => 'Length Required', + '412' => 'Precondition Failed', + '413' => 'Request Entity Too Large', + '414' => 'Request-URI Too Long', + '415' => 'Unsupported Media Type', + '416' => 'Requested Range Not Satisfiable', + '417' => 'Expectation Failed', + '500' => 'Internal Server Error', + '501' => 'Not Implemented', + '502' => 'Bad Gateway', + '503' => 'Service Unavailable', + '504' => 'Gateway Timeout', + '505' => 'HTTP Version Not Supported' + ); + + $code = (string)$code; + + if(array_key_exists($code, $statuscodes)) { + return $statuscodes[$code]; + } else { + return $code; + } + } +}