ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/experimental/reports/graphing/watch.pl
Revision: 1.2
Committed: Sun Mar 10 00:26:24 2002 UTC (22 years, 7 months ago) by tdb
Content type: text/plain
Branch: MAIN
Changes since 1.1: +219 -253 lines
Log Message:
Rewritten most of the processing part. Now uses subroutines to avoid a lot
of unnecessary code duplication. Much tidier, and easier to follow. Maybe
this, and graph.pl, would benefit from an external configuration? maybe...
Still need to tidy the connection handling part, it could bail out without
much notice. It would be better if it kept retrying - ihost style ;)

File Contents

# User Rev Content
1 tdb 1.1 #!/usr/bin/perl -w
2    
3 tdb 1.2 # -----------------------------------------------------------
4     # i-scream graph generation scripts
5     # http://www.i-scream.org.uk
6     #
7     # Generates rrd databases for i-scream data by connecting to
8     # the i-scream server and collecting data.
9     #
10     # $Author$
11     # $Id$
12     #------------------------------------------------------------
13    
14 tdb 1.1 $| = 1;
15    
16     use strict;
17     use iscream::XMLParser;
18     use IO::Socket;
19     use RRDs;
20    
21 tdb 1.2 # Base directory for images
22     # (a directory will be constructed for each host under this)
23     my($imgdir) = "/home/tdb/public_html/rrd";
24    
25     # Location of RRD databases
26     my($rrddir) = "/u1/i-scream/rrd";
27    
28     # for reference:
29     # ch -> hex: $hex = sprintf("%02x", ord($ch));
30     # hex -> ch: $ch = chr(hex($hex));
31    
32     # / converted to a decimal then hex'd
33     my($hex_slash) = "_2f";
34     # _ converted to a decimal then hex'd
35     my($hex_underscore) = "_5f";
36    
37 tdb 1.1 if (@ARGV != 2) {
38     die "Usage: ihost.pl [i-scream client interface] [TCP port]\n";
39     }
40    
41     my($addr) = $ARGV[0];
42     my($cport) = $ARGV[1];
43    
44     my($csock) = new IO::Socket::INET(
45     PeerAddr => $addr,
46     PeerPort => $cport,
47     Proto => 'tcp'
48     ) or die "Cannot connect!";
49    
50     if (!defined $csock) {
51     print "ERROR: Could not connect to $addr:$cport.\n";
52     print "Please check that there is an i-scream server at this address.\n";
53     exit(1);
54     }
55    
56     my($response);
57    
58     $response = <$csock>;
59     if ($response && $response ne "PROTOCOL 1.1\n") {
60     print "The i-scream server sent an unexpected protocol id: $response\n";
61     close($csock);
62     exit(1);
63     }
64    
65     print $csock "cpugrapher\n";
66     $response = <$csock>;
67     if ($response && $response ne "OK\n") {
68     print "Received unexpected response: $response\n";
69     close($csock);
70     exit(1);
71     }
72    
73     print $csock "STARTDATA\n";
74     $response = <$csock>;
75    
76     chop $response;
77     print "Asked to connect to port $response on $addr, connecting...\n";
78    
79     my($dport) = $response;
80    
81     my($dsock) = new IO::Socket::INET(
82     PeerAddr => $addr,
83     PeerPort => $dport,
84     Proto => 'tcp'
85     ) or die "Cannot connect!";
86    
87     if (!defined $dsock) {
88     print "ERROR: Could not connect to $addr:$dport.\n";
89     print "Failure in communications.\n";
90     close($csock);
91     exit(1);
92     }
93    
94 tdb 1.2 ## below here has been "improved"
95     ## above is still a mess ;)
96    
97 tdb 1.1 while(1) {
98 tdb 1.2 # read data
99 tdb 1.1 $response = <$dsock>;
100 tdb 1.2
101     # attemp to parse the data
102 tdb 1.1 my($err, %xmlhash) = &iscream::XMLParser::parse($response);
103     if($err) {
104 tdb 1.2 print STDERR "Skipped, XML did not parse: $response";
105     next;
106 tdb 1.1 }
107 tdb 1.2
108     # standard data packet
109 tdb 1.1 if($xmlhash{"packet.attributes.type"} eq "data") {
110     my($machine) = $xmlhash{"packet.attributes.machine_name"};
111     my($date) = $xmlhash{"packet.attributes.date"};
112 tdb 1.2
113 tdb 1.1 # make directory for machine
114 tdb 1.2 if(! -d "$rrddir/$machine") {
115 tdb 1.1 # not sure on this umask, but it seems to work?
116 tdb 1.2 mkdir "$rrddir/$machine", 0777;
117 tdb 1.1 }
118 tdb 1.2
119     my(@data);
120    
121 tdb 1.1 # cpu
122 tdb 1.2 @data = ( "packet.cpu.idle:idle:GAUGE",
123     "packet.cpu.user:user:GAUGE",
124     "packet.cpu.kernel:kernel:GAUGE",
125     "packet.cpu.swap:swap:GAUGE",
126     "packet.cpu.iowait:iowait:GAUGE",
127     );
128     &updaterrd($machine, "cpu", $date, 15, \%xmlhash, @data);
129    
130 tdb 1.1 # mem
131 tdb 1.2 @data = ( "packet.memory.free:free:GAUGE",
132     "packet.memory.total:total:GAUGE",
133     );
134     &updaterrd($machine, "mem", $date, 15, \%xmlhash, @data);
135    
136 tdb 1.1 # load
137 tdb 1.2 @data = ( "packet.load.load1:load1:GAUGE",
138     "packet.load.load5:load5:GAUGE",
139     "packet.load.load15:load15:GAUGE",
140     );
141     &updaterrd($machine, "load", $date, 15, \%xmlhash, @data);
142    
143 tdb 1.1 # processes
144 tdb 1.2 @data = ( "packet.processes.cpu:cpu:GAUGE",
145     "packet.processes.sleeping:sleeping:GAUGE",
146     "packet.processes.stopped:stopped:GAUGE",
147     "packet.processes.total:total:GAUGE",
148     "packet.processes.zombie:zombie:GAUGE",
149     );
150     &updaterrd($machine, "proc", $date, 15, \%xmlhash, @data);
151    
152 tdb 1.1 # swap
153 tdb 1.2 @data = ( "packet.swap.free:free:GAUGE",
154     "packet.swap.total:total:GAUGE",
155     );
156     &updaterrd($machine, "swap", $date, 15, \%xmlhash, @data);
157    
158 tdb 1.1 # users
159 tdb 1.2 @data = ( "packet.users.count:count:GAUGE",
160     );
161     &updaterrd($machine, "users", $date, 15, \%xmlhash, @data);
162    
163 tdb 1.1 # disk
164     my($i) = 0;
165     while(defined $xmlhash{"packet.disk.p$i.attributes.mount"}) {
166     my($mount) = $xmlhash{"packet.disk.p$i.attributes.mount"};
167 tdb 1.2 $mount =~ s/_/$hex_underscore/g;
168     $mount =~ s/\//$hex_slash/g;
169     @data = ( "packet.disk.p$i.attributes.kbytes:kbytes:GAUGE",
170     "packet.disk.p$i.attributes.used:used:GAUGE",
171     );
172     &updaterrd($machine, "disk-$mount", $date, 15, \%xmlhash, @data);
173 tdb 1.1 ++$i;
174     }
175     }
176 tdb 1.2
177     # queue statistics packet
178 tdb 1.1 elsif($xmlhash{"packet.attributes.type"} eq "queueStat") {
179 tdb 1.2 # psuedo machine for internal server stuff
180     my($machine) = "i-scream-server";
181 tdb 1.1 # make directory
182 tdb 1.2 if(! -d "$rrddir/$machine") {
183 tdb 1.1 # not sure on this umask, but it seems to work?
184 tdb 1.2 mkdir "$rrddir/$machine", 0777;
185 tdb 1.1 }
186 tdb 1.2 my($hash) = $xmlhash{"packet.attributes.hashCode"};
187     my($date) = $xmlhash{"packet.attributes.date"};
188     my($name) = $xmlhash{"packet.attributes.name"};
189 tdb 1.1 # take a look to see if we have a shutdown packet...
190     if($xmlhash{"packet.attributes.shutdown"} && $xmlhash{"packet.attributes.shutdown"} eq "true") {
191 tdb 1.2 unlink <$rrddir/$machine/$hash\_*.rrd>, "$rrddir/$machine/$hash.def", <$imgdir/$machine/$hash*.png>;
192 tdb 1.1 next;
193     }
194 tdb 1.2 # look through to see how many internal queues we have
195 tdb 1.1 my($i) = 0;
196     while(defined $xmlhash{"packet.queue.attributes.queue$i"}) {
197     # see if the queue has been removed
198 tdb 1.2 if($xmlhash{"packet.queue.attributes.queue$i"} eq "[deleted]") {
199     # delete the queues rrd
200     unlink "$rrddir/$machine/$hash\_$i.rrd";
201     # are there any other rrd's left on this queue? if not, cleanup.
202     # get a list of any that may be still there..
203     opendir(DIR, "$rrddir/$machine");
204     my(@rrdcountfiles) = grep { -f "$rrddir/$machine/$_" && /^$hash\_\d+.rrd$/ } readdir(DIR);
205     closedir DIR;
206     # count them (+1 because an empty array is size -1)
207     my($rrdcount) = $#rrdcountfiles + 1;
208 tdb 1.1 if($rrdcount == 0) {
209 tdb 1.2 # clean up the def file and any images
210     unlink "$rrddir/$machine/$hash.def", <$imgdir/$machine/$hash*.png>;
211 tdb 1.1 }
212 tdb 1.2 ++$i;
213     next;
214 tdb 1.1 }
215 tdb 1.2 # the &updaterrd will also do this check, but we want
216     # to write our def file out first
217     if( ! -f "$rrddir/$machine/$hash\_$i.rrd" ) {
218     open(DEF, ">$rrddir/$machine/$hash.def");
219     print DEF $name;
220     close DEF;
221 tdb 1.1 }
222 tdb 1.2 my(@data) = ( "packet.queue.attributes.queue$i:size:GAUGE",
223     "packet.queue.attributes.total:total:GAUGE",
224     );
225     &updaterrd($machine, "$hash\_$i", $date, 15, \%xmlhash, @data);
226 tdb 1.1 ++$i;
227     }
228     }
229     else {
230 tdb 1.2 #print "SKIPPED: valid xml, but not a data or statistics packet\n";
231 tdb 1.1 }
232     }
233    
234 tdb 1.2 # we'll never reach here... unless 1 becomes false for some reason ;)
235 tdb 1.1 exit 0;
236    
237    
238 tdb 1.2 #
239     # sub to update an rrd file
240     #
241     # $machine = name of the machine
242     # (eg. kernow.ukc.ac.uk)
243     # $type = the type of graph for the machine
244     # (eg. cpu)
245     # $date = the date of the item we want to add
246     # (in seconds since the epoch)
247     # $step = the interval at which the database steps
248     # used when we create a new rrd
249     # $xmlref = reference to the xml data packet
250     # @data = array containing data items to add
251     # (eg. "packet.cpu.user:user:GAUGE")
252     #
253     sub updaterrd() {
254     my($machine, $type, $date, $step, $xmlref, @data) = @_;
255     # get hold of the xmlhash we have a reference to
256     my(%xmlhash) = %$xmlref;
257     # check if we need to create a new rrd
258     if( ! -f "$rrddir/$machine/$type.rrd") {
259     my(@createdata);
260     # pull the details out of the data we've been given
261     foreach my $dataitem (@data) {
262     if($dataitem =~ /^\S+:(\S+):(\S+)$/) {
263     push @createdata, "$1:$2";
264     }
265     }
266     # call the &makerrd to actually create the rrd
267     print "making new rrd for $rrddir/$machine/$type.rrd\n";
268     &makerrd($machine, $type, $date, $step, @createdata);
269     }
270     # get the details out of the data we've been given
271     my($updateparams) = "$date";
272     foreach my $dataitem (@data) {
273     if($dataitem =~ /^(\S+):\S+:\S+$/) {
274     # pull the values straight out of the xmlhash
275     $updateparams .= ":$xmlhash{$1}";
276     }
277     }
278     # perform the update
279     RRDs::update ("$rrddir/$machine/$type.rrd", $updateparams);
280     my($err) = RRDs::error;
281     print STDERR "Error updating $rrddir/$machine/$type.rrd: $err\n" if $err;
282     }
283    
284     #
285     # sub to create a new rrd file
286     #
287     # $machine = name of the machine
288     # (eg. kernow.ukc.ac.uk)
289     # $type = the type of graph for the machine
290     # (eg. cpu)
291     # $start = the date of the first item we want to add
292     # (in seconds since the epoch)
293     # $step = the interval at which the database steps
294     # @data = the data items we want to put in the rrd
295     # in the form: $dsname:dstype
296     # (eg. "size:GAUGE")
297     #
298     sub makerrd() {
299     my($machine, $type, $start, $step, @data) = @_;
300     # check if directory exists for rrd
301     if(! -d "$rrddir/$machine") {
302     # not sure on this umask, but it seems to work?
303     mkdir "$rrddir/$machine", 0777;
304     }
305     my(@rrdcmd);
306     # we'll want to add our first data item at $start,
307     # so we start our rrd $step before that.
308     $start -= $step;
309     push @rrdcmd, "$rrddir/$machine/$type.rrd";
310     push @rrdcmd, "--start=$start";
311     push @rrdcmd, "--step=$step";
312     foreach my $dataitem (@data) {
313     # dataitem should be: "dsname:dstype"
314     if($dataitem =~ /^(\S+):(\S+)$/) {
315     push @rrdcmd, "DS:$1:$2:600:U:U";
316     }
317     }
318     push @rrdcmd, (
319     # 3h in 15s samples
320     "RRA:AVERAGE:0.5:1:720",
321     "RRA:MAX:0.5:1:720",
322     # 1d in 2m samples
323     "RRA:AVERAGE:0.5:8:720",
324     "RRA:MAX:0.5:8:720",
325     # 1w in 15m samples
326     "RRA:AVERAGE:0.5:60:672",
327     "RRA:MAX:0.5:60:672",
328     # 1m in 1hr samples
329     "RRA:AVERAGE:0.5:240:744",
330     "RRA:MAX:0.5:60:744",
331     );
332     RRDs::create (@rrdcmd);
333     my($err) = RRDs::error;
334     print STDERR "Error creating rrd for $rrddir/$machine/$type: $err\n" if $err;
335 tdb 1.1 }