Code

Added mysql_{commands,handler} to `collection.cgi'
[collectd.git] / contrib / museum / collection.cgi
1 #!/usr/bin/perl
3 use strict;
4 use warnings;
6 no warnings ('qw');
8 use CGI;
9 use RRDs;
10 use Fcntl (qw(:flock));
11 use Carp (qw(carp cluck confess croak));
13 our $Config = read_config ();
15 our $AbsDir;
16 our $RelDir;
17 our $Type;
18 our $Inst;
19 our $TimeSpan;
21 for (qw(Red Green Blue Yellow Cyan Magenta))
22 {
23         $Config->{'Colors'}{"Half$_"} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
24                 $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{"Full$_"});
25 }
27 $Config->{'Colors'}{'HalfBlueGreen'} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
28         $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{'FullGreen'}, $Config->{'Colors'}{'FullBlue'});
29 $Config->{'Colors'}{'HalfRedBlue'} = color_calculate_transparent ($Config->{'Colors'}{'Alpha'},
30         $Config->{'Colors'}{'Canvas'}, $Config->{'Colors'}{'FullBlue'}, $Config->{'Colors'}{'FullRed'});
32 our $GraphDefs;
33 {
34         my $Alpha  = $Config->{'Colors'}{'Alpha'};
35         my $Canvas = $Config->{'Colors'}{'Canvas'};
37         my $FullRed    = $Config->{'Colors'}{'FullRed'};
38         my $FullGreen  = $Config->{'Colors'}{'FullGreen'};
39         my $FullBlue   = $Config->{'Colors'}{'FullBlue'};
40         my $FullYellow = $Config->{'Colors'}{'FullYellow'};
41         my $FullCyan   = $Config->{'Colors'}{'FullCyan'};
42         my $FullMagenta= $Config->{'Colors'}{'FullMagenta'};
44         my $HalfRed    = $Config->{'Colors'}{'HalfRed'};
45         my $HalfGreen  = $Config->{'Colors'}{'HalfGreen'};
46         my $HalfBlue   = $Config->{'Colors'}{'HalfBlue'};
47         my $HalfYellow = $Config->{'Colors'}{'HalfYellow'};
48         my $HalfCyan   = $Config->{'Colors'}{'HalfCyan'};
49         my $HalfMagenta= $Config->{'Colors'}{'HalfMagenta'};
51         my $HalfBlueGreen = $Config->{'Colors'}{'HalfBlueGreen'};
52         my $HalfRedBlue   = $Config->{'Colors'}{'HalfRedBlue'};
53         
54         $GraphDefs =
55         {
56                 cpu => ['DEF:user_avg={file}:user:AVERAGE',
57                         'DEF:user_min={file}:user:MIN',
58                         'DEF:user_max={file}:user:MAX',
59                         'DEF:nice_avg={file}:nice:AVERAGE',
60                         'DEF:nice_min={file}:nice:MIN',
61                         'DEF:nice_max={file}:nice:MAX',
62                         'DEF:syst_avg={file}:syst:AVERAGE',
63                         'DEF:syst_min={file}:syst:MIN',
64                         'DEF:syst_max={file}:syst:MAX',
65                         'DEF:idle_avg={file}:idle:AVERAGE',
66                         'DEF:idle_min={file}:idle:MIN',
67                         'DEF:idle_max={file}:idle:MAX',
68                         'DEF:wait_avg={file}:wait:AVERAGE',
69                         'DEF:wait_min={file}:wait:MIN',
70                         'DEF:wait_max={file}:wait:MAX',
71                         'CDEF:user_avg_notnull=user_avg,UN,0,user_avg,IF',
72                         'CDEF:nice_avg_notnull=nice_avg,UN,0,nice_avg,IF',
73                         'CDEF:syst_avg_notnull=syst_avg,UN,0,syst_avg,IF',
74                         'CDEF:idle_avg_notnull=idle_avg,UN,0,idle_avg,IF',
75                         'CDEF:wait_avg_notnull=wait_avg,UN,0,wait_avg,IF',
76                         'CDEF:totl_avg_notnull=user_avg_notnull,nice_avg_notnull,+,syst_avg_notnull,+,idle_avg_notnull,+,wait_avg_notnull,+',
77                         'CDEF:user_avg_pct=user_avg_notnull,100,*,totl_avg_notnull,/',
78                         'CDEF:nice_avg_pct=nice_avg_notnull,100,*,totl_avg_notnull,/',
79                         'CDEF:syst_avg_pct=syst_avg_notnull,100,*,totl_avg_notnull,/',
80                         'CDEF:wait_avg_pct=wait_avg_notnull,100,*,totl_avg_notnull,/',
81                         'CDEF:nice_acc=syst_avg_pct,wait_avg_pct,user_avg_pct,nice_avg_pct,+,+,+',
82                         'CDEF:user_acc=syst_avg_pct,wait_avg_pct,user_avg_pct,+,+',
83                         'CDEF:wait_acc=syst_avg_pct,wait_avg_pct,+',
84                         'CDEF:syst_acc=syst_avg_pct',
85 #                       'CDEF:nice_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,nice_avg_notnull,+,+,+',
86 #                       'CDEF:user_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,+,+',
87 #                       'CDEF:wait_acc=syst_avg_notnull,wait_avg_notnull,+',
88 #                       'CDEF:syst_acc=syst_avg_notnull',
89                         "AREA:nice_acc#$HalfGreen",
90                         "AREA:user_acc#$HalfBlue",
91                         "AREA:wait_acc#$HalfYellow",
92                         "AREA:syst_acc#$HalfRed",
93                         "LINE1:nice_acc#$FullGreen:Nice   ",
94                         'GPRINT:nice_min:MIN:%5.1lf%% Min,',
95                         'GPRINT:nice_avg:AVERAGE:%5.1lf%% Avg,',
96                         'GPRINT:nice_max:MAX:%5.1lf%% Max,',
97                         'GPRINT:nice_avg:LAST:%5.1lf%% Last\l',
98                         "LINE1:user_acc#$FullBlue:User   ",
99                         'GPRINT:user_min:MIN:%5.1lf%% Min,',
100                         'GPRINT:user_avg:AVERAGE:%5.1lf%% Avg,',
101                         'GPRINT:user_max:MAX:%5.1lf%% Max,',
102                         'GPRINT:user_avg:LAST:%5.1lf%% Last\l',
103                         "LINE1:wait_acc#$FullYellow:Wait-IO",
104                         'GPRINT:wait_min:MIN:%5.1lf%% Min,',
105                         'GPRINT:wait_avg:AVERAGE:%5.1lf%% Avg,',
106                         'GPRINT:wait_max:MAX:%5.1lf%% Max,',
107                         'GPRINT:wait_avg:LAST:%5.1lf%% Last\l',
108                         "LINE1:syst_acc#$FullRed:System ",
109                         'GPRINT:syst_min:MIN:%5.1lf%% Min,',
110                         'GPRINT:syst_avg:AVERAGE:%5.1lf%% Avg,',
111                         'GPRINT:syst_max:MAX:%5.1lf%% Max,',
112                         'GPRINT:syst_avg:LAST:%5.1lf%% Last\l'
113                 ],
114                 df => [
115                         'DEF:free_avg={file}:free:AVERAGE',
116                         'DEF:free_min={file}:free:MIN',
117                         'DEF:free_max={file}:free:MAX',
118                         'DEF:used_avg={file}:used:AVERAGE',
119                         'DEF:used_min={file}:used:MIN',
120                         'DEF:used_max={file}:used:MAX',
121                         'CDEF:total=free_avg,used_avg,+',
122                         'CDEF:free_pct=100,free_avg,*,total,/',
123                         'CDEF:used_pct=100,used_avg,*,total,/',
124                         'CDEF:free_acc=free_pct,used_pct,+',
125                         'CDEF:used_acc=used_pct',
126                         "AREA:free_acc#$HalfGreen",
127                         "AREA:used_acc#$HalfRed",
128                         "LINE1:free_acc#$FullGreen:Free",
129                         'GPRINT:free_min:MIN:%5.1lf%sB Min,',
130                         'GPRINT:free_avg:AVERAGE:%5.1lf%sB Avg,',
131                         'GPRINT:free_max:MAX:%5.1lf%sB Max,',
132                         'GPRINT:free_avg:LAST:%5.1lf%sB Last\l',
133                         "LINE1:used_acc#$FullRed:Used",
134                         'GPRINT:used_min:MIN:%5.1lf%sB Min,',
135                         'GPRINT:used_avg:AVERAGE:%5.1lf%sB Avg,',
136                         'GPRINT:used_max:MAX:%5.1lf%sB Max,',
137                         'GPRINT:used_avg:LAST:%5.1lf%sB Last\l'
138                 ],
139                 disk => [
140                         'DEF:rtime_avg={file}:rtime:AVERAGE',
141                         'DEF:rtime_min={file}:rtime:MIN',
142                         'DEF:rtime_max={file}:rtime:MAX',
143                         'DEF:wtime_avg={file}:wtime:AVERAGE',
144                         'DEF:wtime_min={file}:wtime:MIN',
145                         'DEF:wtime_max={file}:wtime:MAX',
146                         'CDEF:rtime_avg_ms=rtime_avg,1000,/',
147                         'CDEF:rtime_min_ms=rtime_min,1000,/',
148                         'CDEF:rtime_max_ms=rtime_max,1000,/',
149                         'CDEF:wtime_avg_ms=wtime_avg,1000,/',
150                         'CDEF:wtime_min_ms=wtime_min,1000,/',
151                         'CDEF:wtime_max_ms=wtime_max,1000,/',
152                         'CDEF:total_avg_ms=rtime_avg_ms,wtime_avg_ms,+',
153                         'CDEF:total_min_ms=rtime_min_ms,wtime_min_ms,+',
154                         'CDEF:total_max_ms=rtime_max_ms,wtime_max_ms,+',
155                         "AREA:total_max_ms#$HalfRed",
156                         "AREA:total_min_ms#$Canvas",
157                         "LINE1:wtime_avg_ms#$FullGreen:Write",
158                         'GPRINT:wtime_min_ms:MIN:%5.1lf%s Min,',
159                         'GPRINT:wtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
160                         'GPRINT:wtime_max_ms:MAX:%5.1lf%s Max,',
161                         'GPRINT:wtime_avg_ms:LAST:%5.1lf%s Last\n',
162                         "LINE1:rtime_avg_ms#$FullBlue:Read ",
163                         'GPRINT:rtime_min_ms:MIN:%5.1lf%s Min,',
164                         'GPRINT:rtime_avg_ms:AVERAGE:%5.1lf%s Avg,',
165                         'GPRINT:rtime_max_ms:MAX:%5.1lf%s Max,',
166                         'GPRINT:rtime_avg_ms:LAST:%5.1lf%s Last\n',
167                         "LINE1:total_avg_ms#$FullRed:Total",
168                         'GPRINT:total_min_ms:MIN:%5.1lf%s Min,',
169                         'GPRINT:total_avg_ms:AVERAGE:%5.1lf%s Avg,',
170                         'GPRINT:total_max_ms:MAX:%5.1lf%s Max,',
171                         'GPRINT:total_avg_ms:LAST:%5.1lf%s Last'
172                 ],
173                 hddtemp => [
174                         'DEF:temp_avg={file}:value:AVERAGE',
175                         'DEF:temp_min={file}:value:MIN',
176                         'DEF:temp_max={file}:value:MAX',
177                         "AREA:temp_max#$HalfBlue",
178                         "AREA:temp_min#$Canvas",
179                         "LINE1:temp_avg#$FullBlue:Temperature",
180                         'GPRINT:temp_min:MIN:%4.1lf Min,',
181                         'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
182                         'GPRINT:temp_max:MAX:%4.1lf Max,',
183                         'GPRINT:temp_avg:LAST:%4.1lf Last\l'
184                 ],
185                 load => ['DEF:s_avg={file}:shortterm:AVERAGE',
186                         'DEF:s_min={file}:shortterm:MIN',
187                         'DEF:s_max={file}:shortterm:MAX',
188                         'DEF:m_avg={file}:midterm:AVERAGE',
189                         'DEF:m_min={file}:midterm:MIN',
190                         'DEF:m_max={file}:midterm:MAX',
191                         'DEF:l_avg={file}:longterm:AVERAGE',
192                         'DEF:l_min={file}:longterm:MIN',
193                         'DEF:l_max={file}:longterm:MAX',
194                         "AREA:s_max#$HalfGreen",
195                         "AREA:s_min#$Canvas",
196                         "LINE1:s_avg#$FullGreen: 1m average",
197                         'GPRINT:s_min:MIN:%4.2lf Min,',
198                         'GPRINT:s_avg:AVERAGE:%4.2lf Avg,',
199                         'GPRINT:s_max:MAX:%4.2lf Max,',
200                         'GPRINT:s_avg:LAST:%4.2lf Last\n',
201                         "LINE1:m_avg#$FullBlue: 5m average",
202                         'GPRINT:m_min:MIN:%4.2lf Min,',
203                         'GPRINT:m_avg:AVERAGE:%4.2lf Avg,',
204                         'GPRINT:m_max:MAX:%4.2lf Max,',
205                         'GPRINT:m_avg:LAST:%4.2lf Last\n',
206                         "LINE1:l_avg#$FullRed:15m average",
207                         'GPRINT:l_min:MIN:%4.2lf Min,',
208                         'GPRINT:l_avg:AVERAGE:%4.2lf Avg,',
209                         'GPRINT:l_max:MAX:%4.2lf Max,',
210                         'GPRINT:l_avg:LAST:%4.2lf Last'
211                 ],
212                 mails => ['DEF:rawgood={file}:good:AVERAGE',
213                         'DEF:rawspam={file}:spam:AVERAGE',
214                         'CDEF:good=rawgood,UN,0,rawgood,IF',
215                         'CDEF:spam=rawspam,UN,0,rawspam,IF',
216                         'CDEF:negspam=spam,-1,*',
217                         "AREA:good#$HalfGreen",
218                         "LINE1:good#$FullGreen:Good mails",
219                         'GPRINT:good:AVERAGE:%4.1lf Avg,',
220                         'GPRINT:good:MAX:%4.1lf Max,',
221                         'GPRINT:good:LAST:%4.1lf Last\n',
222                         "AREA:negspam#$HalfRed",
223                         "LINE1:negspam#$FullRed:Spam mails",
224                         'GPRINT:spam:AVERAGE:%4.1lf Avg,',
225                         'GPRINT:spam:MAX:%4.1lf Max,',
226                         'GPRINT:spam:LAST:%4.1lf Last',
227                         'HRULE:0#000000'],
228                 memory => [
229                         'DEF:used_avg={file}:used:AVERAGE',
230                         'DEF:free_avg={file}:free:AVERAGE',
231                         'DEF:buffers_avg={file}:buffers:AVERAGE',
232                         'DEF:cached_avg={file}:cached:AVERAGE',
233                         'DEF:used_min={file}:used:MIN',
234                         'DEF:free_min={file}:free:MIN',
235                         'DEF:buffers_min={file}:buffers:MIN',
236                         'DEF:cached_min={file}:cached:MIN',
237                         'DEF:used_max={file}:used:MAX',
238                         'DEF:free_max={file}:free:MAX',
239                         'DEF:buffers_max={file}:buffers:MAX',
240                         'DEF:cached_max={file}:cached:MAX',
241                         'CDEF:free_cached_buffers_used=free_avg,cached_avg,+,buffers_avg,+,used_avg,+',
242                         'CDEF:cached_buffers_used=cached_avg,buffers_avg,+,used_avg,+',
243                         'CDEF:buffers_used=buffers_avg,used_avg,+',
244                         "AREA:free_cached_buffers_used#$HalfGreen",
245                         "AREA:cached_buffers_used#$HalfBlue",
246                         "AREA:buffers_used#$HalfYellow",
247                         "AREA:used_avg#$HalfRed",
248                         "LINE1:free_cached_buffers_used#$FullGreen:Free        ",
249                         'GPRINT:free_min:MIN:%5.1lf%s Min,',
250                         'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
251                         'GPRINT:free_max:MAX:%5.1lf%s Max,',
252                         'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
253                         "LINE1:cached_buffers_used#$FullBlue:Page cache  ",
254                         'GPRINT:cached_min:MIN:%5.1lf%s Min,',
255                         'GPRINT:cached_avg:AVERAGE:%5.1lf%s Avg,',
256                         'GPRINT:cached_max:MAX:%5.1lf%s Max,',
257                         'GPRINT:cached_avg:LAST:%5.1lf%s Last\n',
258                         "LINE1:buffers_used#$FullYellow:Buffer cache",
259                         'GPRINT:buffers_min:MIN:%5.1lf%s Min,',
260                         'GPRINT:buffers_avg:AVERAGE:%5.1lf%s Avg,',
261                         'GPRINT:buffers_max:MAX:%5.1lf%s Max,',
262                         'GPRINT:buffers_avg:LAST:%5.1lf%s Last\n',
263                         "LINE1:used_avg#$FullRed:Used        ",
264                         'GPRINT:used_min:MIN:%5.1lf%s Min,',
265                         'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
266                         'GPRINT:used_max:MAX:%5.1lf%s Max,',
267                         'GPRINT:used_avg:LAST:%5.1lf%s Last'
268                 ],
269                 mysql_commands => [
270                         "DEF:val_avg={file}:value:AVERAGE",
271                         "DEF:val_min={file}:value:MIN",
272                         "DEF:val_max={file}:value:MAX",
273                         "AREA:val_max#$HalfBlue",
274                         "AREA:val_min#$Canvas",
275                         "LINE1:val_avg#$FullBlue:{inst}",
276                         'GPRINT:val_min:MIN:%5.2lf Min,',
277                         'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
278                         'GPRINT:val_max:MAX:%5.2lf Max,',
279                         'GPRINT:val_avg:LAST:%5.2lf Last'
280                 ],
281                 mysql_handler => [
282                         "DEF:val_avg={file}:value:AVERAGE",
283                         "DEF:val_min={file}:value:MIN",
284                         "DEF:val_max={file}:value:MAX",
285                         "AREA:val_max#$HalfBlue",
286                         "AREA:val_min#$Canvas",
287                         "LINE1:val_avg#$FullBlue:{inst}",
288                         'GPRINT:val_min:MIN:%5.2lf Min,',
289                         'GPRINT:val_avg:AVERAGE:%5.2lf Avg,',
290                         'GPRINT:val_max:MAX:%5.2lf Max,',
291                         'GPRINT:val_avg:LAST:%5.2lf Last'
292                 ],
293                 nfs3_procedures => [
294                         "DEF:null_avg={file}:null:AVERAGE",
295                         "DEF:getattr_avg={file}:getattr:AVERAGE",
296                         "DEF:setattr_avg={file}:setattr:AVERAGE",
297                         "DEF:lookup_avg={file}:lookup:AVERAGE",
298                         "DEF:access_avg={file}:access:AVERAGE",
299                         "DEF:readlink_avg={file}:readlink:AVERAGE",
300                         "DEF:read_avg={file}:read:AVERAGE",
301                         "DEF:write_avg={file}:write:AVERAGE",
302                         "DEF:create_avg={file}:create:AVERAGE",
303                         "DEF:mkdir_avg={file}:mkdir:AVERAGE",
304                         "DEF:symlink_avg={file}:symlink:AVERAGE",
305                         "DEF:mknod_avg={file}:mknod:AVERAGE",
306                         "DEF:remove_avg={file}:remove:AVERAGE",
307                         "DEF:rmdir_avg={file}:rmdir:AVERAGE",
308                         "DEF:rename_avg={file}:rename:AVERAGE",
309                         "DEF:link_avg={file}:link:AVERAGE",
310                         "DEF:readdir_avg={file}:readdir:AVERAGE",
311                         "DEF:readdirplus_avg={file}:readdirplus:AVERAGE",
312                         "DEF:fsstat_avg={file}:fsstat:AVERAGE",
313                         "DEF:fsinfo_avg={file}:fsinfo:AVERAGE",
314                         "DEF:pathconf_avg={file}:pathconf:AVERAGE",
315                         "DEF:commit_avg={file}:commit:AVERAGE",
316                         "DEF:null_max={file}:null:MAX",
317                         "DEF:getattr_max={file}:getattr:MAX",
318                         "DEF:setattr_max={file}:setattr:MAX",
319                         "DEF:lookup_max={file}:lookup:MAX",
320                         "DEF:access_max={file}:access:MAX",
321                         "DEF:readlink_max={file}:readlink:MAX",
322                         "DEF:read_max={file}:read:MAX",
323                         "DEF:write_max={file}:write:MAX",
324                         "DEF:create_max={file}:create:MAX",
325                         "DEF:mkdir_max={file}:mkdir:MAX",
326                         "DEF:symlink_max={file}:symlink:MAX",
327                         "DEF:mknod_max={file}:mknod:MAX",
328                         "DEF:remove_max={file}:remove:MAX",
329                         "DEF:rmdir_max={file}:rmdir:MAX",
330                         "DEF:rename_max={file}:rename:MAX",
331                         "DEF:link_max={file}:link:MAX",
332                         "DEF:readdir_max={file}:readdir:MAX",
333                         "DEF:readdirplus_max={file}:readdirplus:MAX",
334                         "DEF:fsstat_max={file}:fsstat:MAX",
335                         "DEF:fsinfo_max={file}:fsinfo:MAX",
336                         "DEF:pathconf_max={file}:pathconf:MAX",
337                         "DEF:commit_max={file}:commit:MAX",
338                         "CDEF:other_avg=null_avg,readlink_avg,create_avg,mkdir_avg,symlink_avg,mknod_avg,remove_avg,rmdir_avg,rename_avg,link_avg,readdir_avg,readdirplus_avg,fsstat_avg,fsinfo_avg,pathconf_avg,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
339                         "CDEF:other_max=null_max,readlink_max,create_max,mkdir_max,symlink_max,mknod_max,remove_max,rmdir_max,rename_max,link_max,readdir_max,readdirplus_max,fsstat_max,fsinfo_max,pathconf_max,+,+,+,+,+,+,+,+,+,+,+,+,+,+",
340                         "CDEF:stack_read=read_avg",
341                         "CDEF:stack_getattr=stack_read,getattr_avg,+",
342                         "CDEF:stack_access=stack_getattr,access_avg,+",
343                         "CDEF:stack_lookup=stack_access,lookup_avg,+",
344                         "CDEF:stack_write=stack_lookup,write_avg,+",
345                         "CDEF:stack_commit=stack_write,commit_avg,+",
346                         "CDEF:stack_setattr=stack_commit,setattr_avg,+",
347                         "CDEF:stack_other=stack_setattr,other_avg,+",
348                         "AREA:stack_other#$HalfRed",
349                         "AREA:stack_setattr#$HalfGreen",
350                         "AREA:stack_commit#$HalfYellow",
351                         "AREA:stack_write#$HalfGreen",
352                         "AREA:stack_lookup#$HalfBlue",
353                         "AREA:stack_access#$HalfMagenta",
354                         "AREA:stack_getattr#$HalfCyan",
355                         "AREA:stack_read#$HalfBlue",
356                         "LINE1:stack_other#$FullRed:Other  ",
357                         'GPRINT:other_max:MAX:%5.1lf Max,',
358                         'GPRINT:other_avg:AVERAGE:%5.1lf Avg,',
359                         'GPRINT:other_avg:LAST:%5.1lf Last\l',
360                         "LINE1:stack_setattr#$FullGreen:setattr",
361                         'GPRINT:setattr_max:MAX:%5.1lf Max,',
362                         'GPRINT:setattr_avg:AVERAGE:%5.1lf Avg,',
363                         'GPRINT:setattr_avg:LAST:%5.1lf Last\l',
364                         "LINE1:stack_commit#$FullYellow:commit ",
365                         'GPRINT:commit_max:MAX:%5.1lf Max,',
366                         'GPRINT:commit_avg:AVERAGE:%5.1lf Avg,',
367                         'GPRINT:commit_avg:LAST:%5.1lf Last\l',
368                         "LINE1:stack_write#$FullGreen:write  ",
369                         'GPRINT:write_max:MAX:%5.1lf Max,',
370                         'GPRINT:write_avg:AVERAGE:%5.1lf Avg,',
371                         'GPRINT:write_avg:LAST:%5.1lf Last\l',
372                         "LINE1:stack_lookup#$FullBlue:lookup ",
373                         'GPRINT:lookup_max:MAX:%5.1lf Max,',
374                         'GPRINT:lookup_avg:AVERAGE:%5.1lf Avg,',
375                         'GPRINT:lookup_avg:LAST:%5.1lf Last\l',
376                         "LINE1:stack_access#$FullMagenta:access ",
377                         'GPRINT:access_max:MAX:%5.1lf Max,',
378                         'GPRINT:access_avg:AVERAGE:%5.1lf Avg,',
379                         'GPRINT:access_avg:LAST:%5.1lf Last\l',
380                         "LINE1:stack_getattr#$FullCyan:getattr",
381                         'GPRINT:getattr_max:MAX:%5.1lf Max,',
382                         'GPRINT:getattr_avg:AVERAGE:%5.1lf Avg,',
383                         'GPRINT:getattr_avg:LAST:%5.1lf Last\l',
384                         "LINE1:stack_read#$FullBlue:read   ",
385                         'GPRINT:read_max:MAX:%5.1lf Max,',
386                         'GPRINT:read_avg:AVERAGE:%5.1lf Avg,',
387                         'GPRINT:read_avg:LAST:%5.1lf Last\l'
388                 ],
389                 partition => [
390                         "DEF:rbyte_avg={file}:rbytes:AVERAGE",
391                         "DEF:rbyte_min={file}:rbytes:MIN",
392                         "DEF:rbyte_max={file}:rbytes:MAX",
393                         "DEF:wbyte_avg={file}:wbytes:AVERAGE",
394                         "DEF:wbyte_min={file}:wbytes:MIN",
395                         "DEF:wbyte_max={file}:wbytes:MAX",
396                         'CDEF:overlap=wbyte_avg,rbyte_avg,GT,rbyte_avg,wbyte_avg,IF',
397                         "AREA:wbyte_avg#$HalfGreen",
398                         "AREA:rbyte_avg#$HalfBlue",
399                         "AREA:overlap#$HalfBlueGreen",
400                         "LINE1:wbyte_avg#$FullGreen:Write",
401                         'GPRINT:wbyte_min:MIN:%5.1lf%s Min,',
402                         'GPRINT:wbyte_avg:AVERAGE:%5.1lf%s Avg,',
403                         'GPRINT:wbyte_max:MAX:%5.1lf%s Max,',
404                         'GPRINT:wbyte_avg:LAST:%5.1lf%s Last\l',
405                         "LINE1:rbyte_avg#$FullBlue:Read ",
406                         'GPRINT:rbyte_min:MIN:%5.1lf%s Min,',
407                         'GPRINT:rbyte_avg:AVERAGE:%5.1lf%s Avg,',
408                         'GPRINT:rbyte_max:MAX:%5.1lf%s Max,',
409                         'GPRINT:rbyte_avg:LAST:%5.1lf%s Last\l'
410                 ],
411                 ping => ['DEF:ping_avg={file}:ping:AVERAGE',
412                         'DEF:ping_min={file}:ping:MIN',
413                         'DEF:ping_max={file}:ping:MAX',
414                         "AREA:ping_max#$HalfBlue",
415                         "AREA:ping_min#$Canvas",
416                         "LINE1:ping_avg#$FullBlue:Ping",
417                         'GPRINT:ping_min:MIN:%4.1lf ms Min,',
418                         'GPRINT:ping_avg:AVERAGE:%4.1lf ms Avg,',
419                         'GPRINT:ping_max:MAX:%4.1lf ms Max,',
420                         'GPRINT:ping_avg:LAST:%4.1lf ms Last'],
421                 processes => [
422                         "DEF:running_avg={file}:running:AVERAGE",
423                         "DEF:running_min={file}:running:MIN",
424                         "DEF:running_max={file}:running:MAX",
425                         "DEF:sleeping_avg={file}:sleeping:AVERAGE",
426                         "DEF:sleeping_min={file}:sleeping:MIN",
427                         "DEF:sleeping_max={file}:sleeping:MAX",
428                         "DEF:zombies_avg={file}:zombies:AVERAGE",
429                         "DEF:zombies_min={file}:zombies:MIN",
430                         "DEF:zombies_max={file}:zombies:MAX",
431                         "DEF:stopped_avg={file}:stopped:AVERAGE",
432                         "DEF:stopped_min={file}:stopped:MIN",
433                         "DEF:stopped_max={file}:stopped:MAX",
434                         "DEF:paging_avg={file}:paging:AVERAGE",
435                         "DEF:paging_min={file}:paging:MIN",
436                         "DEF:paging_max={file}:paging:MAX",
437                         "DEF:blocked_avg={file}:blocked:AVERAGE",
438                         "DEF:blocked_min={file}:blocked:MIN",
439                         "DEF:blocked_max={file}:blocked:MAX",
440                         'CDEF:paging_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,paging_avg,+,+,+,+,+',
441                         'CDEF:blocked_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,blocked_avg,+,+,+,+',
442                         'CDEF:zombies_acc=sleeping_avg,running_avg,stopped_avg,zombies_avg,+,+,+',
443                         'CDEF:stopped_acc=sleeping_avg,running_avg,stopped_avg,+,+',
444                         'CDEF:running_acc=sleeping_avg,running_avg,+',
445                         'CDEF:sleeping_acc=sleeping_avg',
446                         "AREA:paging_acc#$HalfYellow",
447                         "AREA:blocked_acc#$HalfCyan",
448                         "AREA:zombies_acc#$HalfRed",
449                         "AREA:stopped_acc#$HalfMagenta",
450                         "AREA:running_acc#$HalfGreen",
451                         "AREA:sleeping_acc#$HalfBlue",
452                         "LINE1:paging_acc#$FullYellow:Paging  ",
453                         'GPRINT:paging_min:MIN:%5.1lf Min,',
454                         'GPRINT:paging_avg:AVERAGE:%5.1lf Average,',
455                         'GPRINT:paging_max:MAX:%5.1lf Max,',
456                         'GPRINT:paging_avg:LAST:%5.1lf Last\l',
457                         "LINE1:blocked_acc#$FullCyan:Blocked ",
458                         'GPRINT:blocked_min:MIN:%5.1lf Min,',
459                         'GPRINT:blocked_avg:AVERAGE:%5.1lf Average,',
460                         'GPRINT:blocked_max:MAX:%5.1lf Max,',
461                         'GPRINT:blocked_avg:LAST:%5.1lf Last\l',
462                         "LINE1:zombies_acc#$FullRed:Zombies ",
463                         'GPRINT:zombies_min:MIN:%5.1lf Min,',
464                         'GPRINT:zombies_avg:AVERAGE:%5.1lf Average,',
465                         'GPRINT:zombies_max:MAX:%5.1lf Max,',
466                         'GPRINT:zombies_avg:LAST:%5.1lf Last\l',
467                         "LINE1:stopped_acc#$FullMagenta:Stopped ",
468                         'GPRINT:stopped_min:MIN:%5.1lf Min,',
469                         'GPRINT:stopped_avg:AVERAGE:%5.1lf Average,',
470                         'GPRINT:stopped_max:MAX:%5.1lf Max,',
471                         'GPRINT:stopped_avg:LAST:%5.1lf Last\l',
472                         "LINE1:running_acc#$FullGreen:Running ",
473                         'GPRINT:running_min:MIN:%5.1lf Min,',
474                         'GPRINT:running_avg:AVERAGE:%5.1lf Average,',
475                         'GPRINT:running_max:MAX:%5.1lf Max,',
476                         'GPRINT:running_avg:LAST:%5.1lf Last\l',
477                         "LINE1:sleeping_acc#$FullBlue:Sleeping",
478                         'GPRINT:sleeping_min:MIN:%5.1lf Min,',
479                         'GPRINT:sleeping_avg:AVERAGE:%5.1lf Average,',
480                         'GPRINT:sleeping_max:MAX:%5.1lf Max,',
481                         'GPRINT:sleeping_avg:LAST:%5.1lf Last\l'
482                 ],
483                 sensors => [
484                         'DEF:temp_avg={file}:value:AVERAGE',
485                         'DEF:temp_min={file}:value:MIN',
486                         'DEF:temp_max={file}:value:MAX',
487                         "AREA:temp_max#$HalfBlue",
488                         "AREA:temp_min#$Canvas",
489                         "LINE1:temp_avg#$FullBlue:Value",
490                         'GPRINT:temp_min:MIN:%4.1lf Min,',
491                         'GPRINT:temp_avg:AVERAGE:%4.1lf Avg,',
492                         'GPRINT:temp_max:MAX:%4.1lf Max,',
493                         'GPRINT:temp_avg:LAST:%4.1lf Last\l'
494                 ],
495                 swap => [
496                         'DEF:used_avg={file}:used:AVERAGE',
497                         'DEF:used_min={file}:used:MIN',
498                         'DEF:used_max={file}:used:MAX',
499                         'DEF:free_avg={file}:free:AVERAGE',
500                         'DEF:free_min={file}:free:MIN',
501                         'DEF:free_max={file}:free:MAX',
502                         'DEF:cach_avg={file}:cached:AVERAGE',
503                         'DEF:cach_min={file}:cached:MIN',
504                         'DEF:cach_max={file}:cached:MAX',
505                         'DEF:resv_avg={file}:resv:AVERAGE',
506                         'DEF:resv_min={file}:resv:MIN',
507                         'DEF:resv_max={file}:resv:MAX',
508                         'CDEF:cach_avg_notnull=cach_avg,UN,0,cach_avg,IF',
509                         'CDEF:resv_avg_notnull=resv_avg,UN,0,resv_avg,IF',
510                         'CDEF:used_acc=used_avg',
511                         'CDEF:resv_acc=used_acc,resv_avg_notnull,+',
512                         'CDEF:cach_acc=resv_acc,cach_avg_notnull,+',
513                         'CDEF:free_acc=cach_acc,free_avg,+',
514                         "AREA:free_acc#$HalfGreen",
515                         "AREA:cach_acc#$HalfBlue",
516                         "AREA:resv_acc#$HalfYellow",
517                         "AREA:used_acc#$HalfRed",
518                         "LINE1:free_acc#$FullGreen:Free    ",
519                         'GPRINT:free_min:MIN:%5.1lf%s Min,',
520                         'GPRINT:free_avg:AVERAGE:%5.1lf%s Avg,',
521                         'GPRINT:free_max:MAX:%5.1lf%s Max,',
522                         'GPRINT:free_avg:LAST:%5.1lf%s Last\n',
523                         "LINE1:cach_acc#$FullBlue:Cached  ",
524                         'GPRINT:cach_min:MIN:%5.1lf%s Min,',
525                         'GPRINT:cach_avg:AVERAGE:%5.1lf%s Avg,',
526                         'GPRINT:cach_max:MAX:%5.1lf%s Max,',
527                         'GPRINT:cach_avg:LAST:%5.1lf%s Last\l',
528                         "LINE1:resv_acc#$FullYellow:Reserved",
529                         'GPRINT:resv_min:MIN:%5.1lf%s Min,',
530                         'GPRINT:resv_avg:AVERAGE:%5.1lf%s Avg,',
531                         'GPRINT:resv_max:MAX:%5.1lf%s Max,',
532                         'GPRINT:resv_avg:LAST:%5.1lf%s Last\n',
533                         "LINE1:used_acc#$FullRed:Used    ",
534                         'GPRINT:used_min:MIN:%5.1lf%s Min,',
535                         'GPRINT:used_avg:AVERAGE:%5.1lf%s Avg,',
536                         'GPRINT:used_max:MAX:%5.1lf%s Max,',
537                         'GPRINT:used_avg:LAST:%5.1lf%s Last\l'
538                 ],
539                 traffic => ['DEF:out_min_raw={file}:outgoing:MIN',
540                         'DEF:out_avg_raw={file}:outgoing:AVERAGE',
541                         'DEF:out_max_raw={file}:outgoing:MAX',
542                         'DEF:inc_min_raw={file}:incoming:MIN',
543                         'DEF:inc_avg_raw={file}:incoming:AVERAGE',
544                         'DEF:inc_max_raw={file}:incoming:MAX',
545                         'CDEF:out_min=out_min_raw,8,*',
546                         'CDEF:out_avg=out_avg_raw,8,*',
547                         'CDEF:out_max=out_max_raw,8,*',
548                         'CDEF:inc_min=inc_min_raw,8,*',
549                         'CDEF:inc_avg=inc_avg_raw,8,*',
550                         'CDEF:inc_max=inc_max_raw,8,*',
551                         'CDEF:overlap=out_avg,inc_avg,GT,inc_avg,out_avg,IF',
552                         'CDEF:mytime=out_avg_raw,TIME,TIME,IF',
553                         'CDEF:sample_len_raw=mytime,PREV(mytime),-',
554                         'CDEF:sample_len=sample_len_raw,UN,0,sample_len_raw,IF',
555                         'CDEF:out_avg_sample=out_avg_raw,UN,0,out_avg_raw,IF,sample_len,*',
556                         'CDEF:out_avg_sum=PREV,UN,0,PREV,IF,out_avg_sample,+',
557                         'CDEF:inc_avg_sample=inc_avg_raw,UN,0,inc_avg_raw,IF,sample_len,*',
558                         'CDEF:inc_avg_sum=PREV,UN,0,PREV,IF,inc_avg_sample,+',
559                         "AREA:out_avg#$HalfGreen",
560                         "AREA:inc_avg#$HalfBlue",
561                         "AREA:overlap#$HalfBlueGreen",
562                         "LINE1:out_avg#$FullGreen:Outgoing",
563                         'GPRINT:out_avg:AVERAGE:%5.1lf%s Avg,',
564                         'GPRINT:out_max:MAX:%5.1lf%s Max,',
565                         'GPRINT:out_avg:LAST:%5.1lf%s Last',
566                         'GPRINT:out_avg_sum:LAST:(ca. %5.1lf%sB Total)\l',
567                         "LINE1:inc_avg#$FullBlue:Incoming",
568                         #'GPRINT:inc_min:MIN:%5.1lf %s Min,',
569                         'GPRINT:inc_avg:AVERAGE:%5.1lf%s Avg,',
570                         'GPRINT:inc_max:MAX:%5.1lf%s Max,',
571                         'GPRINT:inc_avg:LAST:%5.1lf%s Last',
572                         'GPRINT:inc_avg_sum:LAST:(ca. %5.1lf%sB Total)\l'
573                 ],
574                 cpufreq => [
575                         'DEF:cpufreq_avg={file}:value:AVERAGE',
576                         'DEF:cpufreq_min={file}:value:MIN',
577                         'DEF:cpufreq_max={file}:value:MAX',
578                         "AREA:cpufreq_max#$HalfBlue",
579                         "AREA:cpufreq_min#$Canvas",
580                         "LINE1:cpufreq_avg#$FullBlue:Frequency",
581                         'GPRINT:cpufreq_min:MIN:%5.1lf%s Min,',
582                         'GPRINT:cpufreq_avg:AVERAGE:%5.1lf%s Avg,',
583                         'GPRINT:cpufreq_max:MAX:%5.1lf%s Max,',
584                         'GPRINT:cpufreq_avg:LAST:%5.1lf%s Last\l'
585                 ],
586                 users => [
587                             'DEF:users_avg={file}:users:AVERAGE',
588                             'DEF:users_min={file}:users:MIN',
589                             'DEF:users_max={file}:users:MAX',
590                             "AREA:users_max#$HalfBlue",
591                             "AREA:users_min#$Canvas",
592                             "LINE1:users_avg#$FullBlue:Users",
593                             'GPRINT:users_min:MIN:%4.1lf Min,',
594                             'GPRINT:users_avg:AVERAGE:%4.1lf Average,',
595                             'GPRINT:users_max:MAX:%4.1lf Max,',
596                             'GPRINT:users_avg:LAST:%4.1lf Last\l'
597                 ]
598         };
599         $GraphDefs->{'disk'} = $GraphDefs->{'partition'};
600         $GraphDefs->{'meminfo'} = $GraphDefs->{'memory'};
603 our $GraphArgs =
605         cpu => ['-t', '{host} cpu{inst} usage', '-v', 'Percent', '-l', '0'],
606         cpufreq => ['-t', '{host} cpu{inst} usage', '-v', 'Mhz'],
607         #disk => ['-t', '{host} disk {inst} IO wait', '-v', 'Seconds'],
608         df => ['-t', '{host}:{inst} usage', '-v', 'Percent', '-l', '0'],
609         disk => ['-t', '{host} disk {inst} usage', '-v', 'Byte/s'],
610         hddtemp => ['-t', '{host} hdd temperature {inst}', '-v', '°Celsius'],
611         load => ['-t', '{host} load average', '-v', 'System load', '-X', '0'],
612         mails   => ['-t', '{host} mail count', '-v', 'Amount', '-X', '0'],
613         memory => ['-t', '{host} memory usage', '-v', 'Bytes', '-b', '1024', '-l', '0'],
614         mysql_commands => ['-t', 'mysql command {inst}', '-v', 'Issues/s' ],
615         mysql_handler => ['-t', 'mysql handler {inst}', '-v', 'Issues/s' ],
616         nfs3_procedures => ['-t', '{host} NFSv3 {inst} procedures', '-v', 'Procedures/s' ],
617         partition => ['-t', '{host} partition {inst} usage', '-v', 'Byte/s'],
618         ping => ['-t', '{host} ping to {inst}', '-v', 'ms'],
619         processes => ['-t', '{host} processes', '-v', 'Processes'],
620         sensors => ['-t', '{host} sensor {inst}', '-v', '°Celsius'],
621         swap => ['-t', '{host} swap usage', '-v', 'Bytes', '-b', '1024', '-l', '0'],
622         traffic => ['-t', '{host} {inst} traffic', '-v', 'Bit/s'],
623     users => ['-t', '{host} users', '-v', 'Users'],
624 };
626 our $GraphMulti =
628         cpu     => \&output_graph_cpu,
629         cpufreq => 1,
630         disk    => 1,
631         load    => 0,
632         mails   => 0,
633         memory  => 0,
634         mysql_commands => \&output_graph_mysql_commands,
635         mysql_handler => \&output_graph_mysql_handler,
636         partition => 1,
637         ping    => \&output_graph_ping,
638         sensors => 1,
639         traffic => 1,
640     users => 1
641 };
643 our @Info;
644 if (defined ($ENV{'GATEWAY_INTERFACE'}))
646         @Info = ($ENV{'PATH_INFO'} || '') =~ m#([\w\-\.]+)#g;
648 else
650         @Info = @ARGV;
653 parse_pathinfo (@Info);
655 if ($TimeSpan)
657         output_graph ();
659 else
661         output_page ();
664 exit (0);
666 sub output_graph_cpu
668         my @inst = @_;
669         my @ret = ();
671         die if (@inst < 2);
673         for (@inst)
674         {
675                 push (@ret,
676                         "DEF:user_avg_$_=$AbsDir/cpu-$_.rrd:user:AVERAGE",
677                         "DEF:user_min_$_=$AbsDir/cpu-$_.rrd:user:MIN",
678                         "DEF:user_max_$_=$AbsDir/cpu-$_.rrd:user:MAX",
679                         "DEF:nice_avg_$_=$AbsDir/cpu-$_.rrd:nice:AVERAGE",
680                         "DEF:nice_min_$_=$AbsDir/cpu-$_.rrd:nice:MIN",
681                         "DEF:nice_max_$_=$AbsDir/cpu-$_.rrd:nice:MAX",
682                         "DEF:syst_avg_$_=$AbsDir/cpu-$_.rrd:syst:AVERAGE",
683                         "DEF:syst_min_$_=$AbsDir/cpu-$_.rrd:syst:MIN",
684                         "DEF:syst_max_$_=$AbsDir/cpu-$_.rrd:syst:MAX",
685                         "DEF:wait_avg_$_=$AbsDir/cpu-$_.rrd:wait:AVERAGE",
686                         "DEF:wait_min_$_=$AbsDir/cpu-$_.rrd:wait:MIN",
687                         "DEF:wait_max_$_=$AbsDir/cpu-$_.rrd:wait:MAX");
688         }
690         for (qw(user nice syst wait))
691         {
692                 my $def = $_;
693                 my $cdef;
695                 my $default_value = ($def eq 'user' or $def eq 'syst') ? 'UNKN' : '0';
697                 for (qw(avg min max))
698                 {
699                         my $cf = $_;
701                         for (@inst)
702                         {
703                                 push (@ret, "CDEF:${def}_${cf}_notnull_${_}=${def}_${cf}_${_},UN,0,${def}_${cf}_${_},IF");
704                                 push (@ret, "CDEF:${def}_${cf}_defined_${_}=${def}_${cf}_${_},UN,0,1,IF");
705                         }
707                         $cdef = "CDEF:${def}_${cf}_num=" . join (',', map { "${def}_${cf}_defined_${_}" } (@inst));
708                         $cdef .= ',+' x (scalar (@inst) - 1);
709                         push (@ret, $cdef);
711                         $cdef = "CDEF:${def}_${cf}=${def}_${cf}_num," . join (',', map { "${def}_${cf}_notnull_${_}" } (@inst));
712                         $cdef .= ',+' x (scalar (@inst) - 1);
713                         $cdef .= ",${def}_${cf}_num,${def}_${cf}_num,1,IF,/,$default_value,IF";
714                         push (@ret, $cdef);
715                         push (@ret, "CDEF:${def}_${cf}_notnull=${def}_${cf},UN,0,${def}_${cf},IF");
716                 }
717         }
719         push (@ret,
720                 "CDEF:nice_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,nice_avg_notnull,+,+,+",
721                 "CDEF:user_acc=syst_avg_notnull,wait_avg_notnull,user_avg_notnull,+,+",
722                 "CDEF:wait_acc=syst_avg_notnull,wait_avg_notnull,+",
723                 "CDEF:syst_acc=syst_avg_notnull");
725         push (@ret, grep { $_ !~ m/^C?DEF/ } (@{$GraphDefs->{'cpu'}}));
727         return (@ret);
730 sub output_graph_ping
732         my @inst = @_;
733         my @ret = ();
735         die if (@inst < 2);
737         my @colors = get_n_colors (scalar (@inst));
739         for (my $i = 0; $i < scalar (@inst); $i++)
740         {
741                 my $inst = $inst[$i];
742                 push (@ret,
743                         "DEF:avg_$i=$AbsDir/ping-$inst.rrd:ping:AVERAGE",
744                         "DEF:min_$i=$AbsDir/ping-$inst.rrd:ping:MIN",
745                         "DEF:max_$i=$AbsDir/ping-$inst.rrd:ping:MAX");
746         }
748         for (my $i = 0; $i < scalar (@inst); $i++)
749         {
750                 my $inst = $inst[$i];
751                 my $color = $colors[$i];
753                 if (length ($inst) > 15)
754                 {
755                         $inst = substr ($inst, 0, 12) . '...';
756                 }
757                 else
758                 {
759                         $inst = sprintf ('%-15s', $inst);
760                 }
762                 push (@ret,
763                         "LINE1:avg_$i#$color:$inst",
764                         "GPRINT:min_$i:MIN:%4.1lf ms Min,",
765                         "GPRINT:avg_$i:AVERAGE:%4.1lf ms Avg,",
766                         "GPRINT:max_$i:MAX:%4.1lf ms Max,",
767                         "GPRINT:avg_$i:LAST:%4.1lf ms Last\\l");
768         }
770         return (@ret);
773 sub output_graph_mysql_commands
775         my @inst = @_;
776         my @ret = ();
778         die if (@inst < 2);
780         my @colors = get_n_colors (scalar (@inst));
782         for (my $i = 0; $i < scalar (@inst); $i++)
783         {
784                 my $inst = $inst[$i];
785                 push (@ret,
786                         "DEF:avg_$i=$AbsDir/mysql_commands-$inst.rrd:value:AVERAGE",
787                         "DEF:min_$i=$AbsDir/mysql_commands-$inst.rrd:value:MIN",
788                         "DEF:max_$i=$AbsDir/mysql_commands-$inst.rrd:value:MAX");
789         }
791         for (my $i = 0; $i < scalar (@inst); $i++)
792         {
793                 my $inst = $inst[$i];
794                 my $color = $colors[$i];
796                 if (length ($inst) > 18)
797                 {
798                         $inst = substr ($inst, 0, 15) . '...';
799                 }
800                 else
801                 {
802                         $inst = sprintf ('%-18s', $inst);
803                 }
805                 push (@ret,
806                         "LINE1:avg_$i#$color:$inst",
807                         "GPRINT:min_$i:MIN:%6.1lf Min,",
808                         "GPRINT:avg_$i:AVERAGE:%6.1lf Avg,",
809                         "GPRINT:max_$i:MAX:%6.1lf Max,",
810                         "GPRINT:avg_$i:LAST:%6.1lf Last\\l");
811         }
813         return (@ret);
816 sub output_graph_mysql_handler
818         my @inst = @_;
819         my @ret = ();
821         die if (@inst < 2);
823         my @colors = get_n_colors (scalar (@inst));
825         for (my $i = 0; $i < scalar (@inst); $i++)
826         {
827                 my $inst = $inst[$i];
828                 push (@ret,
829                         "DEF:avg_$i=$AbsDir/mysql_handler-$inst.rrd:value:AVERAGE",
830                         "DEF:min_$i=$AbsDir/mysql_handler-$inst.rrd:value:MIN",
831                         "DEF:max_$i=$AbsDir/mysql_handler-$inst.rrd:value:MAX");
832         }
834         for (my $i = 0; $i < scalar (@inst); $i++)
835         {
836                 my $inst = $inst[$i];
837                 my $color = $colors[$i];
839                 if (length ($inst) > 18)
840                 {
841                         $inst = substr ($inst, 0, 15) . '...';
842                 }
843                 else
844                 {
845                         $inst = sprintf ('%-18s', $inst);
846                 }
848                 push (@ret,
849                         "LINE1:avg_$i#$color:$inst",
850                         "GPRINT:min_$i:MIN:%6.1lf Min,",
851                         "GPRINT:avg_$i:AVERAGE:%6.1lf Avg,",
852                         "GPRINT:max_$i:MAX:%6.1lf Max,",
853                         "GPRINT:avg_$i:LAST:%6.1lf Last\\l");
854         }
856         return (@ret);
859 sub output_graph
861         die unless (defined ($GraphDefs->{$Type}));
863         my $host;
864         my @cmd = ();
865         my $file = $AbsDir . '/';
866         my $files = get_all_files ($AbsDir);
868         #
869         # get hostname
870         #
871         if ($RelDir =~ m#([^/]+)$#)
872         {
873                 $host = $1;
874         }
875         else
876         {
877                 $host = $Config->{'HostName'};
878         }
880         #
881         # get timespan
882         #
883         if ($TimeSpan =~ m/(\d+)/)
884         {
885                 $TimeSpan = -1 * int ($1);
886         }
887         else
888         {
889                 my %t = (hour => -3600, day => -86400, week => -604800, month => -2678400, year => -31622400);
890                 die unless (defined ($t{$TimeSpan}));
891                 $TimeSpan = $t{$TimeSpan};
892         }
894         if (scalar (@{$files->{$Type}}) == 1)
895         {
896                 $Inst = $files->{$Type}[0];
897         }
899         push (@cmd, '-', '-a', 'PNG', '-s', $TimeSpan);
900         push (@cmd, @{$GraphArgs->{$Type}}) if (defined ($GraphArgs->{$Type}));
902         for (qw(Back ShadeA ShadeB Font Canvas Grid MGrid Frame Arrow))
903         {
904                 push (@cmd, '-c', uc ($_) . '#' . $Config->{'Colors'}{$_});
905         }
907         if ((length ($Inst) == 0) and (ref ($GraphMulti->{$Type}) eq 'CODE'))
908         {
909                 push (@cmd, $GraphMulti->{$Type}->(@{$files->{$Type}}));
910         }
911         else
912         {
913                 if (length ("$Inst"))
914                 {
915                         $file .= "$Type-$Inst.rrd";
916                 }
917                 else
918                 {
919                         $file .= "$Type.rrd";
920                 }
922                 die ("File not found: $file") unless (-e $file);
924                 push (@cmd, @{$GraphDefs->{$Type}});
925         }
927         for (@cmd)
928         {
929                 $_ =~ s/{file}/$file/g;
930                 $_ =~ s/{host}/$host/g;
931                 $_ =~ s/{inst}/$Inst/g;
932                 $_ =~ s/{type}/$Type/g;
933         }
935         $| = 1;
937         print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
938 Content-Type: image/png
939 Cache-Control: no-cache
941 HEADER
943         if (1)
944         {
945                 my $fh;
946                 open ($fh, ">/tmp/collection.log") or die ("open: $!");
947                 flock ($fh, LOCK_EX) or die ("flock: $!");
949                 print $fh join ("\n\t", @cmd) . "\n";
951                 close ($fh);
952         }
954         RRDs::graph (@cmd);
956         die ('RRDs::error: ' . RRDs::error ()) if (RRDs::error ());
959 sub output_page
961         my $files = get_all_files ($AbsDir);
962         my $dirs  = get_all_dirs  ($AbsDir);
964         print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
965 Content-Type: text/html
966 Cache-Control: no-cache
968 <html>
969         <head>
970                 <title>Collection: $RelDir</title>
971                 <style type="text/css">
972                         img { border: none; display: block; }
973                 </style>
974         </head>
976         <body>
977 HEADER
979         my $MySelf = defined ($ENV{'GATEWAY_INTERFACE'}) ? $ENV{'SCRIPT_NAME'} : $0;
981         if ((length ($Type) != 0) and (length ($Inst) == 0) and (ref ($GraphMulti->{$Type}) eq 'CODE') and (scalar (@{$files->{$Type}}) > 1))
982         {
983                 print qq(\t\t<div><a href="$MySelf$RelDir">Go up</a></div>\n);
985                 print "\t\t<ul>\n";
986                 for (@{$files->{$Type}})
987                 {
988                         print qq(\t\t\t<li><a href="$MySelf$RelDir/$Type/$_">$_</a></li>\n);
989                 }
990                 print <<HTML;
991                 </ul>
993                 <h3>Daily</h3>
994                 <div><img src="$MySelf$RelDir/$Type/day" /></div>
995                 <h3>Weekly</h3>
996                 <div><img src="$MySelf$RelDir/$Type/week" /></div>
997                 <h3>Monthly</h3>
998                 <div><img src="$MySelf$RelDir/$Type/month" /></div>
999                 <h3>Yearly</h3>
1000                 <div><img src="$MySelf$RelDir/$Type/year" /></div>
1001 HTML
1002         }
1003         elsif (length ($Type) != 0)
1004         {
1005                 my $ext = length ($Inst) ? "$Type/$Inst" : $Type;
1007                 if ((ref ($GraphMulti->{$Type}) eq 'CODE') and (scalar (@{$files->{$Type}}) > 1))
1008                 {
1009                         print qq(<div><a href="$MySelf$RelDir/$Type">Go up</a></div>\n);
1010                 }
1011                 else
1012                 {
1013                         print qq(<div><a href="$MySelf$RelDir">Go up</a></div>\n);
1014                 }
1016                 print <<HTML;
1017                 <h3>Daily</h3>
1018                 <div><img src="$MySelf$RelDir/$ext/day" /></div>
1019                 <h3>Weekly</h3>
1020                 <div><img src="$MySelf$RelDir/$ext/week" /></div>
1021                 <h3>Monthly</h3>
1022                 <div><img src="$MySelf$RelDir/$ext/month" /></div>
1023                 <h3>Yearly</h3>
1024                 <div><img src="$MySelf$RelDir/$ext/year" /></div>
1025 HTML
1026         }
1027         else
1028         {
1029                 if ($RelDir)
1030                 {
1031                         my ($up) = $RelDir =~ m#(.*)/[^/]+$#;
1032                         print qq(\t\t<div><a href="$MySelf$up">Go up</a></div>\n);
1033                 }
1035                 if (@$dirs)
1036                 {
1037                         print "<ul>\n";
1038                         for (@$dirs)
1039                         {
1040                                 print qq(<li>$AbsDir/<a href="$MySelf$RelDir/$_">$_</a></li>\n);
1041                         }
1042                         print "</ul>\n";
1043                 }
1045                 for (sort (keys %$files))
1046                 {
1047                         my $type = $_;
1049                         if (ref ($GraphMulti->{$type}) eq 'CODE')
1050                         {
1051                                 print qq(\t\t<a href="$MySelf$RelDir/$type" />),
1052                                 qq(<img src="$MySelf$RelDir/$type/day" /></a>\n);
1053                                 next;
1054                         }
1056                         for (@{$files->{$type}})
1057                         {
1058                                 my $inst = "$_";
1060                                 if (length ($inst))
1061                                 {
1062                                         print qq(\t\t<a href="$MySelf$RelDir/$type/$inst" />),
1063                                         qq(<img src="$MySelf$RelDir/$type/$inst/day" /></a>\n);
1064                                 }
1065                                 else
1066                                 {
1067                                         print qq(\t\t<a href="$MySelf$RelDir/$type" />),
1068                                         qq(<img src="$MySelf$RelDir/$type/day" /></a>\n);
1069                                 }
1070                         }
1071                 }
1072         }
1074         print STDOUT <<FOOTER if (defined ($ENV{'GATEWAY_INTERFACE'}));
1075         </body>
1076 </html>
1077 FOOTER
1080 sub output_xml
1082         my $files = get_all_files ();
1084         print STDOUT <<HEADER if (defined ($ENV{'GATEWAY_INTERFACE'}));
1085 Content-Type: text/xml
1086 Cache-Control: no-cache
1088 HEADER
1089         print STDOUT pl2xml ($files);
1092 sub read_config
1094         my $file = @_ ? shift : '/etc/collection.conf';
1095         my $conf;
1096         my $fh;
1098 #       if (open ($fh, "< $file"))
1099 #       {
1100 #               my $xml;
1101 #               local $/ = undef;
1102 #               $xml = <$fh>;
1104 #               eval
1105 #               {
1106 #                       $conf = xml2pl ($xml);
1107 #               };
1108 #               close ($fh);
1109 #       }
1111         if (!$conf)
1112         {
1113                 return ({
1114                                 Colors =>
1115                                 {
1116                                         Back    => 'FFFFFF',
1117                                         ShadeA  => 'FFFFFF',
1118                                         ShadeB  => 'FFFFFF',
1119                                         Font    => '000000',
1120                                         Canvas  => 'F5F5F5',
1121                                         Grid    => 'D0D0D0',
1122                                         MGrid   => 'A0A0A0',
1123                                         Frame   => '646464',
1124                                         Arrow   => 'FF0000',
1126                                         FullRed         => 'FF0000',
1127                                         FullBlue        => '0000FF',
1128                                         FullGreen       => '00E000',
1129                                         FullYellow      => 'F0A000',
1130                                         FullCyan        => '00A0FF',
1131                                         FullMagenta     => 'A000FF',
1132                                         Alpha           => 0.25,
1133                                         HalfRed         => 'F8B8B8',
1134                                         HalfBlue        => 'B8B8F8',
1135                                         HalfGreen       => 'B8F0B8',
1136                                         HalfYellow      => 'F4F4B8'
1137                                 },
1138                                 Directory => '/var/lib/collectd',
1139                                 HostName  => (defined ($ENV{'SERVER_NAME'}) ? $ENV{'SERVER_NAME'} : 'localhost')
1140                         });
1141         }
1142         else
1143         {
1144                 return ($conf);
1145         }
1148 sub parse_pathinfo
1150         my @info = @_;
1152         $AbsDir = $Config->{'Directory'};
1153         $RelDir = '';
1155         while (@info and -d $AbsDir . '/' . $Info[0])
1156         {
1157                 my $new = shift (@info);
1158                 next if ($new =~ m/^\./);
1160                 $AbsDir .= '/' . $new;
1161                 $RelDir .= '/' . $new;
1162         }
1164         $Type = '';
1165         $Inst = '';
1166         $TimeSpan = '';
1168         confess ("parse_pathinfo: too many elements in pathinfo") if (scalar (@info) > 3);
1169         return unless (@info);
1171         $Type = shift (@info);
1172         return unless (@info);
1174         if ($info[-1] =~ m/^(hour|day|week|month|year)$/i)
1175         {
1176                 $TimeSpan = pop (@info);
1177         }
1179         $Inst = shift (@info) if (@info);
1181         confess ("unrecognized elements in pathinfo") if (@info);
1184 sub get_all_files
1186         my $dir = @_ ? shift : $Config->{'Directory'};
1187         my $hash = {};
1188         my $dh;
1190         if (opendir ($dh, $dir))
1191         {
1192                 while (my $thing = readdir ($dh))
1193                 {
1194                         next if ($thing =~ m/^\./);
1196                         my $type;
1197                         my $inst;
1199                         if ($thing =~ m/^(\w+)-([\w\-\.]+)\.rrd$/)
1200                         {
1201                                 $type = $1;
1202                                 $inst = $2;
1203                         }
1204                         elsif ($thing =~ m/^(\w+)\.rrd$/)
1205                         {
1206                                 $type = $1;
1207                                 $inst = '';
1208                         }
1209                         else
1210                         {
1211                                 next;
1212                         }
1214                         # Only load RRD files we can actually display..
1215                         next unless (defined ($GraphDefs->{$type}));
1217                         $hash->{$type} = [] unless (defined ($hash->{$type}));
1218                         push (@{$hash->{$type}}, $inst);
1219                 }
1221                 closedir ($dh);
1222         }
1224         return ($hash);
1227 sub get_all_dirs
1229         my $dir = @_ ? shift : $Config->{'Directory'};
1230         my @ret = ();
1231         my $dh;
1233         if (opendir ($dh, $dir))
1234         {
1235                 while (my $thing = readdir ($dh))
1236                 {
1237                         next if ($thing =~ m/^\./);
1239                         next if (!-d "$dir/$thing");
1241                         push (@ret, $thing);
1242                 }
1244                 closedir ($dh);
1245         }
1247         return (@ret) if (wantarray ());
1248         return (\@ret);
1251 sub color_hex2rgb
1253         my $color = shift;
1255         my ($red, $green, $blue) = map { ord (pack ("H2", $_)) } ($color =~ m/([A-Fa-f0-9]{2})/g);
1256         #print STDERR "$color -> rgb($red,$green,$blue)\n";
1258         return ($red, $green, $blue);
1261 sub color_rgb2hex
1263         croak unless (scalar (@_) == 3);
1264         
1265         my ($red, $green, $blue) = @_;
1267         my $ret = sprintf ("%02X%02X%02X", $red, $green, $blue);
1268         #print STDERR "rgb($red,$green,$blue) -> $ret\n";
1270         return ($ret);
1273 sub color_calculate_transparent
1275         my $alpha = shift;
1276         my $canvas = [color_hex2rgb (shift)];
1277         my @colors = map { [color_hex2rgb ($_)] } (@_);
1279         if (($alpha < 0.0) or ($alpha > 1.0))
1280         {
1281                 $alpha = 1.0;
1282         }
1284         if ($alpha == 0.0)
1285         {
1286                 return (color_rgb2hex (@$canvas));
1287         }
1288         if ($alpha == 1.0)
1289         {
1290                 return (color_rgb2hex (@{$colors[-1]}));
1291         }
1293         my $ret = _color_calculate_transparent ($alpha, $canvas, @colors);
1295         return (color_rgb2hex (@$ret));
1298 sub _color_calculate_transparent
1300         my $alpha = shift;
1301         my $canvas = shift;
1302         my $color = shift;
1303         my @colors = @_ ? shift : ();
1304         my $ret = [0, 0, 0];
1306         for (my $i = 0; $i < 3; $i++)
1307         {
1308                 $ret->[$i] = ($alpha * $color->[$i]) + ((1 - $alpha) * $canvas->[$i]);
1309         }
1311         return (_color_calculate_transparent ($alpha, $ret, @colors)) if (@colors);
1312         return ($ret);
1315 sub get_n_colors
1317         my $num = shift;
1318         my @ret = ();
1320         for (my $i = 0; $i < $num; $i++)
1321         {
1322                 my $pos = 6 * $i / $num;
1323                 my $n = int ($pos);
1324                 my $p = $pos - $n;
1325                 my $q = 1 - $p;
1327                 my $red   = 0;
1328                 my $green = 0;
1329                 my $blue  = 0;
1331                 if ($n == 0)
1332                 {
1333                         $red  = 255;
1334                         $blue = 255 * $p;
1335                 }
1336                 elsif ($n == 1)
1337                 {
1338                         $red  = 255 * $q;
1339                         $blue = 255;
1340                 }
1341                 elsif ($n == 2)
1342                 {
1343                         $green = 255 * $p;
1344                         $blue  = 255;
1345                 }
1346                 elsif ($n == 3)
1347                 {
1348                         $green = 255;
1349                         $blue  = 255 * $q;
1350                 }
1351                 elsif ($n == 4)
1352                 {
1353                         $red   = 255 * $p;
1354                         $green = 255;
1355                 }
1356                 elsif ($n == 5)
1357                 {
1358                         $red   = 255;
1359                         $green = 255 * $q;
1360                 }
1361                 else { die; }
1363                 push (@ret, sprintf ("%02x%02x%02x", $red, $green, $blue));
1364         }
1366         return (@ret);