contrib/php-collection: Add a PHP frontend for graph generation.
[collectd.git] / contrib / php-collection / graph.php
1 <?php // vim:fenc=utf-8:filetype=php:ts=4
2 /*
3  * Copyright (C) 2009  Bruno PrĂ©mont <bonbons AT linux-vserver.org>
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation; only version 2 of the License is applicable.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
12  * details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 error_reporting(E_ALL | E_NOTICE | E_WARNING);
20
21 require('config.php');
22 require('functions.php');
23 require('definitions.php');
24
25 function makeTextBlock($text, $fontfile, $fontsize, $width) {
26         // TODO: handle explicit line-break!
27         $words = explode(' ', $text);
28         $lines = array($words[0]);
29         $currentLine = 0;
30         foreach ($words as $word) {
31                 $lineSize = imagettfbbox($fontsize, 0, $fontfile, $lines[$currentLine] . ' ' . $word);
32                 if($lineSize[2] - $lineSize[0] < $width) {
33                         $lines[$currentLine] .= ' ' . $word;
34                 } else {
35                         $currentLine++;
36                         $lines[$currentLine] = $word;
37                 }
38         }
39         error_log(sprintf('Handles message "%s", %d words => %d/%d lines', $text, count($words), $currentLine, count($lines)));
40         return implode("\n", $lines);
41 }
42
43 /**
44  * No RRD files found that could match request
45  * @code HTTP error code
46  * @code_msg Short text description of HTTP error code
47  * @title Title for fake RRD graph
48  * @msg Complete error message to display in place of graph content
49  */
50 function error($code, $code_msg, $title, $msg) {
51         global $config;
52         header(sprintf("HTTP/1.0 %d %s", $code, $code_msg));
53         header("Pragma: no-cache");
54         header("Expires: Mon, 01 Jan 2008 00:00:00 CET");
55         header("Content-Type: image/png");
56         $w = $config['rrd_width']+81;
57         $h = $config['rrd_height']+79;
58
59         $png     = imagecreate($w, $h);
60         $c_bkgnd = imagecolorallocate($png, 240, 240, 240);
61         $c_fgnd  = imagecolorallocate($png, 255, 255, 255);
62         $c_blt   = imagecolorallocate($png, 208, 208, 208);
63         $c_brb   = imagecolorallocate($png, 160, 160, 160);
64         $c_grln  = imagecolorallocate($png, 114, 114, 114);
65         $c_grarr = imagecolorallocate($png, 128,  32,  32);
66         $c_txt   = imagecolorallocate($png, 0, 0, 0);
67         $c_etxt  = imagecolorallocate($png, 64, 0, 0);
68
69         if (function_exists('imageantialias'))
70                 imageantialias($png, true);
71         imagefilledrectangle($png, 0,   0, $w, $h, $c_bkgnd);
72         imagefilledrectangle($png, 51, 33, $w-31, $h-47, $c_fgnd);
73         imageline($png,  51,  30,  51, $h-43, $c_grln);
74         imageline($png,  48, $h-46, $w-28, $h-46, $c_grln);
75         imagefilledpolygon($png, array(49, 30,    51, 26,    53, 30), 3, $c_grarr);
76         imagefilledpolygon($png, array($w-28, $h-48,  $w-24, $h-46,  $w-28, $h-44), 3, $c_grarr);
77         imageline($png,    0,    0,   $w,    0, $c_blt);
78         imageline($png,    0,    1,   $w,    1, $c_blt);
79         imageline($png,    0,    0,    0,   $h, $c_blt);
80         imageline($png,    1,    0,    1,   $h, $c_blt);
81         imageline($png, $w-1,    0, $w-1,   $h, $c_brb);
82         imageline($png, $w-2,    1, $w-2,   $h, $c_brb);
83         imageline($png,    1, $h-2,   $w, $h-2, $c_brb);
84         imageline($png,    0, $h-1,   $w, $h-1, $c_brb);
85
86         imagestring($png, 4, ceil(($w-strlen($title)*imagefontwidth(4)) / 2), 10, $title, $c_txt);
87         imagestring($png, 5, 60, 35, sprintf('%s [%d]', $code_msg, $code), $c_etxt);
88         if (function_exists('imagettfbbox') && is_file($config['error_font'])) {
89                 // Detailled error message
90                 $fmt_msg = makeTextBlock($msg, $errorfont, 10, $w-86);
91                 $fmtbox  = imagettfbbox(12, 0, $errorfont, $fmt_msg);
92                 imagettftext($png, 10, 0, 55, 35+3+imagefontwidth(5)-$fmtbox[7]+$fmtbox[1], $c_txt, $errorfont, $fmt_msg);
93         } else {
94                 imagestring($png, 4, 53, 35+6+imagefontwidth(5), $msg, $c_txt);
95         }
96
97         imagepng($png);
98         imagedestroy($png);
99 }
100
101 /**
102  * No RRD files found that could match request
103  */
104 function error404($title, $msg) {
105         return error(404, "Not found", $title, $msg);
106 }
107
108 /**
109  * Incomplete / invalid request
110  */
111 function error400($title, $msg) {
112         return error(400, "Bad request", $title, $msg);
113 }
114
115 // Process input arguments
116 $host     = read_var('host', $_GET, null);
117 if (is_null($host))
118         return error400("?/?-?/?", "Missing host name");
119 else if (!is_string($host))
120         return error400("?/?-?/?", "Expecting exactly 1 host name");
121 else if (strlen($host) == 0)
122         return error400("?/?-?/?", "Host name may not be blank");
123
124 $plugin   = read_var('plugin', $_GET, null);
125 if (is_null($plugin))
126         return error400($host.'/?-?/?', "Missing plugin name");
127 else if (!is_string($plugin))
128         return error400($host.'/?-?/?', "Plugin name must be a string");
129 else if (strlen($plugin) == 0)
130         return error400($host.'/?-?/?', "Plugin name may not be blank");
131
132 $pinst    = read_var('plugin_instance', $_GET, '');
133 if (!is_string($pinst))
134         return error400($host.'/'.$plugin.'-?/?', "Plugin instance name must be a string");
135
136 $type     = read_var('type', $_GET, '');
137 if (is_null($type))
138         return error400($host.'/'.$plugin.(strlen($pinst) ? '-'.$pinst : '').'/?', "Missing type name");
139 else if (!is_string($type))
140         return error400($host.'/'.$plugin.(strlen($pinst) ? '-'.$pinst : '').'/?', "Type name must be a string");
141 else if (strlen($type) == 0)
142         return error400($host.'/'.$plugin.(strlen($pinst) ? '-'.$pinst : '').'/?', "Type name may not be blank");
143
144 $tinst    = read_var('type_instance', $_GET, '');
145
146 $graph_identifier = $host.'/'.$plugin.(strlen($pinst) ? '-'.$pinst : '').'/'.$type.(strlen($tinst) ? '-'.$tinst : '-*');
147
148 $timespan = read_var('timespan', $_GET, $config['timespan'][0]['name']);
149 $timespan_ok = false;
150 foreach ($config['timespan'] as &$ts)
151         if ($ts['name'] == $timespan)
152                 $timespan_ok = true;
153 if (!$timespan_ok)
154         return error400($graph_identifier, "Unknown timespan requested");
155
156 $logscale   = (boolean)read_var('logarithmic', $_GET, false);
157 $tinylegend = (boolean)read_var('tinylegend', $_GET, false);
158
159 // Check that at least 1 RRD exists for the specified request
160 $all_tinst = collectd_list_tinsts($host, $plugin, $pinst, $type);
161 if (count($all_tinst) == 0)
162         return error404($graph_identifier, "No rrd file found for graphing");
163
164 // Now that we are read, do the bulk work
165 load_graph_definitions($logscale, $tinylegend);
166
167 $pinst = strlen($pinst) == 0 ? null : $pinst;
168 $tinst = strlen($tinst) == 0 ? null : $tinst;
169
170 $opts  = array();
171 $opts['timespan'] = $timespan;
172 if ($logscale)
173         $opts['logarithmic'] = 1;
174 if ($tinylegend)
175         $opts['tinylegend']  = 1;
176
177 $rrd_cmd = false;
178 if (isset($MetaGraphDefs[$type])) {
179         $identifiers = array();
180         foreach ($all_tinst as &$atinst)
181                 $identifiers[] = collectd_identifier($host, $plugin, is_null($pinst) ? '' : $pinst, $type, $atinst);
182         collectd_flush($identifiers);
183         $rrd_cmd = $MetaGraphDefs[$type]($host, $plugin, $pinst, $type, $all_tinst, $opts);
184 } else {
185         if (!in_array(is_null($tinst) ? '' : $tinst, $all_tinst))
186                 return error404($host.'/'.$plugin.(!is_null($pinst) ? '-'.$pinst : '').'/'.$type.(!is_null($tinst) ? '-'.$tinst : ''), "No rrd file found for graphing");
187         collectd_flush(collectd_identifier($host, $plugin, is_null($pinst) ? '' : $pinst, $type, is_null($tinst) ? '' : $tinst));
188         if (isset($GraphDefs[$type]))
189                 $rrd_cmd = collectd_draw_generic($timespan, $host, $plugin, $pinst, $type, $tinst);
190         else
191                 $rrd_cmd = collectd_draw_rrd($host, $plugin, $pinst, $type, $tinst);
192 }
193
194 if (isset($_GET['debug'])) {
195         header('Content-Type: text/plain; charset=utf-8');
196         printf("Would have executed:\n%s\n", $rrd_cmd);
197         return 0;
198 } else if ($rrd_cmd) {
199         header('Content-Type: image/png');
200         header('Cache-Control: max-age=60');
201         $rt = 0;
202         passthru($rrd_cmd, $rt);
203         if ($rt != 0)
204                 return error500($graph_identifier, "RRD failed to generate the graph: ".$rt);
205         return $rt;
206 } else {
207         return error500($graph_identifier, "Failed to tell RRD how to generate the graph");
208 }
209
210 ?>