ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/reports/rrdgraphing/graph.pl
Revision: 1.4
Committed: Tue May 21 11:37:56 2002 UTC (22 years ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.3: +61 -44 lines
Log Message:
Slightly neater stuff. Can now specify to ignore the rrd's rather than
delete, which is probably more sane - you won't lose historical information
for machines which probably will come back one day. Still not sure what to
do about the old .def files, but they don't cause any harm... they just
clutter things up a bit.

File Contents

# Content
1 #!/usr/bin/perl -w
2
3 #
4 # i-scream central monitoring system
5 # Copyright (C) 2000-2002 i-scream
6 #
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 #
21
22 # -----------------------------------------------------------
23 # i-scream graph generation script
24 # http://www.i-scream.org.uk
25 #
26 # Generates graphs from rrd databases for i-scream data.
27 #
28 # $Author: tdb $
29 # $Id: graph.pl,v 1.3 2002/05/20 16:11:23 tdb Exp $
30 #------------------------------------------------------------
31
32 ## TODO
33 # possibly make more configurable?
34 # -- allow configurable periods of graphs
35 # -- comments, types, etc
36 # -- move all to external config file
37
38 $| = 1;
39 use strict;
40 use RRDs;
41
42 # Base directory for images
43 # (a directory will be constructed for each host under this)
44 my($imgdir) = "/home/pkg/iscream/public_html/graphs";
45
46 # Location of RRD databases
47 my($rrddir) = "/u1/i-scream/databases";
48
49 # / converted to a decimal then hex'd
50 my($hex_slash) = "_2f";
51 # _ converted to a decimal then hex'd
52 my($hex_underscore) = "_5f";
53
54 # maximum age (last modified) before an rrd or graph get cleaned up
55 # (in seconds)
56 my($maxrrdage) = 3600; # 1 hour
57 my($maximgage) = 3600; # 1 hour
58
59 # delete rrd's when they get cleaned up?
60 # if unset, will just ignore the rrd's
61 # - usually best to leave this off, we don't want to delete useful rrds :)
62 my($deleterrds) = 0;
63
64 # delete graphs when they get cleaned up?
65 # if unset, won't bother checking at all
66 # - usually best to leave this on
67 my($deleteimgs) = 1;
68
69 # Read the contents of the base directory
70 # and pull out the list of subdirectories (except . and .. :)
71 opendir(DIR, $rrddir);
72 my(@rrddirlist) = grep { -d "$rrddir/$_" && !/^\.$/ && !/^\.\.$/ } readdir(DIR);
73 closedir DIR;
74
75 # look through each directoty, as they might
76 # contain rrds for a particular machine
77 foreach my $machine (@rrddirlist) {
78 # Read the contents of the directory
79 opendir(DIR, "$rrddir/$machine");
80 my(@rrdlist) = grep { /\.rrd$/ && -f "$rrddir/$machine/$_" } readdir(DIR);
81 closedir DIR;
82
83 # See what rrd we have, and generate the graphs accordingly
84 foreach my $rrd (@rrdlist) {
85 chomp $rrd;
86 # stat the file
87 my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
88 $ctime,$blksize,$blocks) = stat("$rrddir/$machine/$rrd");
89 # check if it's old enough to be deleted
90 if((time - $mtime) > $maxrrdage) {
91 # do we delete the rrd, or just ignore it?
92 if($deleterrds) {
93 # if so, delete it
94 unlink("$rrddir/$machine/$rrd");
95 }
96 # no more processing required for this rrd
97 next;
98 }
99 if($rrd =~ /^(cpu)\.rrd$/) {
100 my(@data);
101 my(@rawdata);
102 push @data, "LINE2:$1:idle:idle#00FF00:idle cpu";
103 push @data, "LINE2:$1:user:user#0000FF:user cpu";
104 push @data, "LINE2:$1:kernel:kernel#00FFFF:kernel cpu";
105 push @data, "LINE2:$1:swap:swap#FF00FF:swap cpu";
106 push @data, "LINE2:$1:iowait:iowait#FF0000:iowait cpu";
107 push @rawdata, "--upper-limit=100";
108 &makegraph($machine, $1, "CPU Usage for $machine", \@data, \@rawdata);
109 }
110 if($rrd =~ /^(mem)\.rrd$/) {
111 my(@data);
112 my(@rawdata);
113 # we don't actually want to display free memory,
114 # although we need it to do inuse...
115 push @data, "NONE:$1:free:free#CCCCFF:free memory";
116 push @data, "LINE2:$1:total:total#0000FF:total memory";
117 # calculate inuse
118 push @rawdata, "CDEF:inuse=total,free,-";
119 # and add it to the graph
120 push @rawdata, "AREA:inuse#CCCCFF:memory in use";
121 push @rawdata, "--base=1024";
122 &makegraph($machine, $1, "Memory Usage for $machine", \@data, \@rawdata);
123 }
124 if($rrd =~ /^(load)\.rrd$/) {
125 my(@data);
126 push @data, "LINE2:$1:load1:load1#CCCCFF:1 minute load average";
127 push @data, "LINE2:$1:load5:load5#7777FF:5 minute load average";
128 push @data, "LINE2:$1:load15:load15#0000FF:15 minute load average";
129 &makegraph($machine, $1, "Loads for $machine", \@data);
130 }
131 if($rrd =~ /^(proc)\.rrd$/) {
132 my(@data);
133 push @data, "LINE2:$1:cpu:cpu#00FF00:cpu processes";
134 push @data, "LINE2:$1:sleeping:sleeping#0000FF:sleeping processes";
135 push @data, "LINE2:$1:stopped:stopped#00FFFF:stopped processes";
136 push @data, "LINE2:$1:total:total#FF00FF:total processes";
137 push @data, "LINE2:$1:zombie:zombie#FF0000:zombie processes";
138 &makegraph($machine, $1, "Processes on $machine", \@data);
139 }
140 if($rrd =~ /^(swap)\.rrd$/) {
141 my(@data);
142 my(@rawdata);
143 # we don't actually want to display free swap,
144 # although we need it to do inuse...
145 push @data, "NONE:$1:free:free#CCCCFF:free swap";
146 push @data, "LINE2:$1:total:total#0000FF:total swap";
147 # calculate inuse
148 push @rawdata, "CDEF:inuse=total,free,-";
149 # and add it to the graph
150 push @rawdata, "AREA:inuse#CCCCFF:swap in use";
151 push @rawdata, "--base=1024";
152 &makegraph($machine, $1, "Swap Usage for $machine", \@data, \@rawdata);
153 }
154 if($rrd =~ /^(users)\.rrd$/) {
155 my(@data);
156 push @data, "AREA:$1:count:count#CCCCFF:user count";
157 &makegraph($machine, $1, "User Count for $machine", \@data);
158 }
159 if($rrd =~ /^(disk)-(\S+).rrd$/) {
160 my(@data);
161 my(@rawdata);
162 push @data, "LINE2:$1-$2:kbytes:kbytes#0000FF:total size";
163 push @data, "AREA:$1-$2:used:used#CCCCFF:used";
164 push @rawdata, "--base=1024";
165 my($type) = $1;
166 my($name) = $2;
167 my($nicename) = $2;
168 $nicename =~ s/$hex_slash/\//g;
169 $nicename =~ s/$hex_underscore/_/g;
170 &makegraph($machine, "$type-$name", "Disk Usage for $machine on $nicename", \@data, \@rawdata);
171 }
172 # probably a queue with a name like this :)
173 if($rrd =~ /^(\d+)_0\.rrd$/) {
174 my(@data);
175 my(@rawdata);
176 my($baserrd) = $1;
177 my($i) = 0;
178 while( -f "$rrddir/$machine/$baserrd\_$i.rrd" ) {
179 push @data, "LINE2:$baserrd\_$i:size:size$i" . &get_colour($i) . ":queue$i size ";
180 ++$i;
181 }
182 push @data, "LINE2:$baserrd\_0:total:total#FF0000:packets/sec - currently";
183 push @rawdata, "GPRINT:total:LAST:%lf %spackets/sec";
184 my($comment);
185 if(-f "$rrddir/$machine/$baserrd.def") {
186 open(DEF, "$rrddir/$machine/$baserrd.def");
187 $comment = <DEF>;
188 chomp $comment if defined $comment;
189 }
190 $comment = "unknown queue" if not defined $comment;
191 &makegraph($machine, $baserrd, $comment, \@data, \@rawdata);
192 }
193 }
194 # have a last check, maybe we can remove the directory now?
195 # (only if we're deleting stuff)
196 if($deleterrds) {
197 # Read the contents of the directory
198 opendir(DIR, "$rrddir/$machine");
199 my(@dirlist) = grep { !/^\.$/ && !/^\.\.$/ } readdir(DIR);
200 closedir DIR;
201 if($#dirlist == -1) {
202 rmdir "$rrddir/$machine";
203 }
204 }
205 }
206
207 if($deleteimgs) {
208 # Read the contents of the graphs directory
209 # and pull out the list of subdirectories (except . and .. :)
210 opendir(DIR, $imgdir);
211 my(@imgdirlist) = grep { -d "$imgdir/$_" && !/^\.$/ && !/^\.\.$/ } readdir(DIR);
212 closedir DIR;
213
214 # look through each directoty, as they might
215 # contain images for a particular machine
216 foreach my $machine (@imgdirlist) {
217 # Read the contents of the directory
218 opendir(DIR, "$imgdir/$machine");
219 my(@imglist) = grep { /\.png$/ && -f "$imgdir/$machine/$_" } readdir(DIR);
220 closedir DIR;
221
222 # See what rrd we have, and generate the graphs accordingly
223 foreach my $img (@imglist) {
224 chomp $img;
225 # stat the img
226 my($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,
227 $ctime,$blksize,$blocks) = stat("$imgdir/$machine/$img");
228 # check if it's old enough to be deleted
229 if((time - $mtime) > $maximgage) {
230 # if so, delete it
231 unlink("$imgdir/$machine/$img");
232 }
233 }
234 # have a last check, maybe we can remove the directory now?
235 # Read the contents of the directory
236 opendir(DIR, "$imgdir/$machine");
237 my(@dirlist) = grep { !/^\.$/ && !/^\.\.$/ } readdir(DIR);
238 closedir DIR;
239 if($#dirlist == -1) {
240 rmdir "$imgdir/$machine";
241 }
242 }
243 }
244
245 exit(0);
246
247
248 #
249 # subroutine to make some graphs
250 #
251 # $machine = name of the machine
252 # (eg. kernow.ukc.ac.uk)
253 # $type = the type of graph for the machine
254 # (eg. cpu)
255 # $title = the title for the graph
256 # (eg. kernow CPU usage)
257 # $dataref = a reference to an array containing information for the graph
258 # elements of format: "gtype:rrdname:dsname:name#colour:comment with spaces"
259 # (if gtype is "NONE" only a DEF of 'name' will be defined, no line will be plotted)
260 # $rawcmdref = a reference to an array containing raw rrd commands
261 # elements a single command each, no spaces
262 #
263
264 sub makegraph() {
265 my($machine, $type, $title, $dataref, $rawcmdref) = @_;
266 # pass in these arrays by reference
267 my(@data) = @$dataref if defined $dataref;
268 my(@rawcmd) = @$rawcmdref if defined $rawcmdref;
269 # check if directory exists for images
270 if(! -d "$imgdir/$machine") {
271 # not sure on this umask, but it seems to work?
272 mkdir "$imgdir/$machine", 0777;
273 }
274 my(@rrdcmd);
275 foreach my $dataitem (@data) {
276 # dataitem should be: "gtype:rrdname:dsname:name#colour:comment with spaces"
277 # (if gtype is "NONE" only a DEF of 'name' will be defined, no line will be plotted)
278 if($dataitem =~ /^(\S+):(\S+):(\S+):(\S+)#(.{6}):(.*)$/) {
279 push @rrdcmd, "DEF:$4=$rrddir/$machine/$2.rrd:$3:AVERAGE";
280 if($1 ne "NONE") {
281 push @rrdcmd, "$1:$4#$5:$6";
282 }
283 }
284 }
285 push @rrdcmd, "--title=$title";
286 push @rrdcmd, "--imgformat=PNG";
287 push @rrdcmd, "--lower-limit=0";
288 # not entirely convinced this is good...
289 push @rrdcmd, "--alt-autoscale-max";
290 # add any further raw commands
291 push @rrdcmd, @rawcmd;
292 RRDs::graph ("$imgdir/$machine/$type-3h.png", "--start=-10800", @rrdcmd);
293 my($err_3h) = RRDs::error;
294 print STDERR "Error generating 3h graph for $machine/$type: $err_3h\n" if $err_3h;
295 RRDs::graph ("$imgdir/$machine/$type-1d.png", "--start=-86400", @rrdcmd);
296 my($err_1d) = RRDs::error;
297 print STDERR "Error generating 1d graph for $machine/$type: $err_1d\n" if $err_1d;
298 RRDs::graph ("$imgdir/$machine/$type-1w.png", "--start=-604800", @rrdcmd);
299 my($err_1w) = RRDs::error;
300 print STDERR "Error generating 1w graph for $machine/$type: $err_1w\n" if $err_1w;
301 RRDs::graph ("$imgdir/$machine/$type-1m.png", "--start=-2678400", @rrdcmd);
302 my($err_1m) = RRDs::error;
303 print STDERR "Error generating 1m graph for $machine/$type: $err_1m\n" if $err_1m;
304 RRDs::graph ("$imgdir/$machine/$type-1y.png", "--start=-31536000", @rrdcmd);
305 my($err_1y) = RRDs::error;
306 print STDERR "Error generating 1y graph for $machine/$type: $err_1y\n" if $err_1y;
307 return;
308 }
309
310 # hacky subroutine to return a colour
311 # could be done much better somehow :/
312 sub get_colour {
313 my($col) = @_;
314 if($col == 0) {
315 return "#0000FF";
316 }
317 elsif($col == 1) {
318 return "#00FF00";
319 }
320 elsif($col == 2) {
321 return "#FF00FF";
322 }
323 elsif($col == 3) {
324 return "#FFFF00";
325 }
326 elsif($col == 4) {
327 return "#00FFFF";
328 }
329 else {
330 return "#000066";
331 }
332 }