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'};
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'};
601 }
603 our $GraphArgs =
604 {
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 =
627 {
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'}))
645 {
646 @Info = ($ENV{'PATH_INFO'} || '') =~ m#([\w\-\.]+)#g;
647 }
648 else
649 {
650 @Info = @ARGV;
651 }
653 parse_pathinfo (@Info);
655 if ($TimeSpan)
656 {
657 output_graph ();
658 }
659 else
660 {
661 output_page ();
662 }
664 exit (0);
666 sub output_graph_cpu
667 {
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);
728 }
730 sub output_graph_ping
731 {
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);
771 }
773 sub output_graph_mysql_commands
774 {
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);
814 }
816 sub output_graph_mysql_handler
817 {
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);
857 }
859 sub output_graph
860 {
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 ());
957 }
959 sub output_page
960 {
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
1078 }
1080 sub output_xml
1081 {
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);
1090 }
1092 sub read_config
1093 {
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>;
1103 #
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 }
1146 }
1148 sub parse_pathinfo
1149 {
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);
1182 }
1184 sub get_all_files
1185 {
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);
1225 }
1227 sub get_all_dirs
1228 {
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);
1249 }
1251 sub color_hex2rgb
1252 {
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);
1259 }
1261 sub color_rgb2hex
1262 {
1263 croak unless (scalar (@_) == 3);
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);
1271 }
1273 sub color_calculate_transparent
1274 {
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));
1296 }
1298 sub _color_calculate_transparent
1299 {
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);
1313 }
1315 sub get_n_colors
1316 {
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);
1367 }