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

# Content
1 #!/usr/bin/perl -w
2
3 # -----------------------------------------------------------
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 $| = 1;
15
16 use strict;
17 use iscream::XMLParser;
18 use IO::Socket;
19 use RRDs;
20
21 # 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 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 ## below here has been "improved"
95 ## above is still a mess ;)
96
97 while(1) {
98 # read data
99 $response = <$dsock>;
100
101 # attemp to parse the data
102 my($err, %xmlhash) = &iscream::XMLParser::parse($response);
103 if($err) {
104 print STDERR "Skipped, XML did not parse: $response";
105 next;
106 }
107
108 # standard data packet
109 if($xmlhash{"packet.attributes.type"} eq "data") {
110 my($machine) = $xmlhash{"packet.attributes.machine_name"};
111 my($date) = $xmlhash{"packet.attributes.date"};
112
113 # make directory for machine
114 if(! -d "$rrddir/$machine") {
115 # not sure on this umask, but it seems to work?
116 mkdir "$rrddir/$machine", 0777;
117 }
118
119 my(@data);
120
121 # cpu
122 @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 # mem
131 @data = ( "packet.memory.free:free:GAUGE",
132 "packet.memory.total:total:GAUGE",
133 );
134 &updaterrd($machine, "mem", $date, 15, \%xmlhash, @data);
135
136 # load
137 @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 # processes
144 @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 # swap
153 @data = ( "packet.swap.free:free:GAUGE",
154 "packet.swap.total:total:GAUGE",
155 );
156 &updaterrd($machine, "swap", $date, 15, \%xmlhash, @data);
157
158 # users
159 @data = ( "packet.users.count:count:GAUGE",
160 );
161 &updaterrd($machine, "users", $date, 15, \%xmlhash, @data);
162
163 # 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 $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 ++$i;
174 }
175 }
176
177 # queue statistics packet
178 elsif($xmlhash{"packet.attributes.type"} eq "queueStat") {
179 # psuedo machine for internal server stuff
180 my($machine) = "i-scream-server";
181 # make directory
182 if(! -d "$rrddir/$machine") {
183 # not sure on this umask, but it seems to work?
184 mkdir "$rrddir/$machine", 0777;
185 }
186 my($hash) = $xmlhash{"packet.attributes.hashCode"};
187 my($date) = $xmlhash{"packet.attributes.date"};
188 my($name) = $xmlhash{"packet.attributes.name"};
189 # take a look to see if we have a shutdown packet...
190 if($xmlhash{"packet.attributes.shutdown"} && $xmlhash{"packet.attributes.shutdown"} eq "true") {
191 unlink <$rrddir/$machine/$hash\_*.rrd>, "$rrddir/$machine/$hash.def", <$imgdir/$machine/$hash*.png>;
192 next;
193 }
194 # look through to see how many internal queues we have
195 my($i) = 0;
196 while(defined $xmlhash{"packet.queue.attributes.queue$i"}) {
197 # see if the queue has been removed
198 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 if($rrdcount == 0) {
209 # clean up the def file and any images
210 unlink "$rrddir/$machine/$hash.def", <$imgdir/$machine/$hash*.png>;
211 }
212 ++$i;
213 next;
214 }
215 # 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 }
222 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 ++$i;
227 }
228 }
229 else {
230 #print "SKIPPED: valid xml, but not a data or statistics packet\n";
231 }
232 }
233
234 # we'll never reach here... unless 1 becomes false for some reason ;)
235 exit 0;
236
237
238 #
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 }