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, 7 months 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

# User Rev Content
1 tdb 1.1 #!/usr/bin/perl -w
2    
3 tdb 1.2 #
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 tdb 1.1 # -----------------------------------------------------------
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 tdb 1.4 # $Id: graph.pl,v 1.3 2002/05/20 16:11:23 tdb Exp $
30 tdb 1.1 #------------------------------------------------------------
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 tdb 1.3
54     # maximum age (last modified) before an rrd or graph get cleaned up
55     # (in seconds)
56     my($maxrrdage) = 3600; # 1 hour
57 tdb 1.4 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 tdb 1.3
69 tdb 1.1 # 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 tdb 1.3 # 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 tdb 1.4 # do we delete the rrd, or just ignore it?
92     if($deleterrds) {
93     # if so, delete it
94     unlink("$rrddir/$machine/$rrd");
95     }
96 tdb 1.3 # no more processing required for this rrd
97     next;
98     }
99 tdb 1.1 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 tdb 1.3 }
194     # have a last check, maybe we can remove the directory now?
195 tdb 1.4 # (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 tdb 1.3 }
205     }
206    
207 tdb 1.4 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 tdb 1.3 closedir DIR;
213    
214 tdb 1.4 # 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 tdb 1.3 }
242     }
243 tdb 1.1 }
244 tdb 1.4
245     exit(0);
246    
247 tdb 1.1
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     }