ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/host/ihost-perl/ihost.pl
Revision: 1.32
Committed: Fri Mar 16 18:25:59 2001 UTC (23 years, 7 months ago) by pjm2
Content type: text/plain
Branch: MAIN
Changes since 1.31: +45 -27 lines
Log Message:
Lots of changes in the TCP configuration and TCP heartbeats.
The aim of this is to make the ihost much more resilient when
dealing with broken servers.

File Contents

# Content
1 #!/usr/bin/perl -w
2
3 # -----------------------------------------------------------
4 # Perl i-scream Host.
5 # http://www.i-scream.org.uk
6 #
7 # An all-in-one script to act as an i-scream host on
8 # a typical Unix/Linux box. You may adapt the data-gathering
9 # methods as you see fit.
10 # - pjm2@ukc.ac.uk
11 #
12 # $Author: pjm2 $
13 # $Id: ihost.pl,v 1.31 2001/03/15 09:46:18 pjm2 Exp $
14 #------------------------------------------------------------
15
16 $| = 1;
17
18 use strict;
19 use IO::Socket;
20 use Sys::Hostname;
21
22 use vars qw (
23 $filter_manager_addr
24 $filter_manager_port
25 $seq_no
26 $udp_update_time
27 $tcp_update_time
28 $last_udp_time
29 $last_tcp_time
30 $last_modified
31 $udp_port
32 $tcp_port
33 $filter_addr
34 $file_list
35 $fqdn
36 $pidfile
37 $retry_wait
38 );
39
40 if (@ARGV != 2) {
41 die "Usage: ihost.pl [i-scream filter manager] [TCP port]\n";
42 }
43
44 $filter_manager_addr = $ARGV[0];
45 $filter_manager_port = $ARGV[1];
46
47 $seq_no = 1;
48 $retry_wait = 6;
49
50 # write our PID to a file
51 $pidfile = "/var/tmp/ihost.pid";
52 &write_pid();
53
54 &tcp_configure();
55 &send_udp_packet();
56
57 $last_udp_time = time;
58 $last_tcp_time = time;
59 while (1) {
60 my($time) = time;
61 if ($time >= $last_udp_time + $udp_update_time) {
62 &send_udp_packet();
63 $last_udp_time = $time;
64 }
65 if ($time >= $last_tcp_time + $tcp_update_time) {
66 &send_tcp_heartbeat();
67 $last_tcp_time = $time;
68 }
69 my($next_udp) = $udp_update_time - $time + $last_udp_time;
70 my($next_tcp) = $tcp_update_time - $time + $last_tcp_time;
71 my($delay);
72 if ($next_udp < $next_tcp) {
73 $delay = $next_udp
74 }
75 else {
76 $delay = $next_tcp;
77 }
78 `sleep $delay`;
79 }
80
81 # we'll probably never get here...
82 `rm -f $pidfile`;
83 exit(0);
84
85
86 #-----------------------------------------------------------------------
87 # wait_then_retry
88 # Waits for the period of time specified in $retry_wait, then attempts
89 # to reconfigure with the server.
90 #-----------------------------------------------------------------------
91 sub wait_then_retry() {
92 print "Will retry configuration with filter manager in $retry_wait seconds.\n";
93 `sleep $retry_wait`;
94 }
95
96
97 #-----------------------------------------------------------------------
98 # tcp_configure
99 # Establishes a TCP connection to the specified i-scream filter manager.
100 # The host then requests details from the server, such as the intervals
101 # at which to send UDP packets.
102 #-----------------------------------------------------------------------
103 sub tcp_configure() {
104
105 while (1) {
106 my($sock) = new IO::Socket::INET(
107 PeerAddr => $filter_manager_addr,
108 PeerPort => $filter_manager_port,
109 Proto => 'tcp'
110 ) or die "Cannot connect!";
111 if (!defined $sock) {
112 print "IHOST ERROR: Could not connect to $filter_manager_addr:$filter_manager_port.\n";
113 print "Please check that there is an i-scream server at this address.\n";
114 wait_then_retry();
115 next;
116 }
117
118 # Now run through the configuration process...
119 my($response);
120
121 print $sock "STARTCONFIG\n";
122 $response = <$sock>;
123 if ($response && !($response eq "OK\n")) {
124 print "The i-scream server rejected the STARTCONFIG command.\n";
125 close($sock);
126 wait_then_retry();
127 next;
128 }
129
130 print "Config started okay.\n";
131
132 print $sock "LASTMODIFIED\n";
133 $response = <$sock>;
134 if (!$response) {
135 print "The i-scream server did not return anything for the LASTMODIFIED command.\n";
136 close($sock);
137 wait_then_retry();
138 next;
139 }
140 chop $response;
141 $last_modified = $response;
142
143 print "Config last modified: ". (scalar localtime $last_modified/1000) . "\n";
144
145 print $sock "FILELIST\n";
146 $response = <$sock>;
147 if (!$response) {
148 print "The i-scream server did not provide a configuration file list.\n";
149 close($sock);
150 wait_then_retry();
151 next;
152 }
153 chop $response;
154 $file_list = $response;
155
156 print "File list obtained: $file_list\n";
157
158 print $sock "FQDN\n";
159 $response = <$sock>;
160 if (!$response) {
161 print "The i-scream server did not tell us our FQDN.\n";
162 close($sock);
163 wait_then_retry();
164 next;
165 }
166 chop $response;
167 $fqdn = $response;
168
169 print "FQDN returned: $fqdn\n";
170
171 print $sock "UDPUpdateTime\n";
172 $response = <$sock>;
173 if (!$response) {
174 print "The i-scream server did not give us a UDPUpdateTime.\n";
175 close($sock);
176 wait_then_retry();
177 next;
178 }
179 chop $response;
180 $udp_update_time = $response;
181
182 print $sock "TCPUpdateTime\n";
183 $response = <$sock>;
184 if (!$response) {
185 print "The i-scream server did not give us a TCPUpdateTime.\n";
186 close($sock);
187 wait_then_retry();
188 next;
189 }
190 chop $response;
191 $tcp_update_time = $response;
192
193 print "UDP packet period: $udp_update_time seconds.\nTCP heartbeat period: $tcp_update_time seconds.\n";
194
195 print $sock "ENDCONFIG\n";
196 $response = <$sock>;
197 if ($response && !($response eq "OK\n")) {
198 print "ENDCONFIG command to server failed. Terminated.\n";
199 close($sock);
200 wait_then_retry();
201 next;
202 }
203
204 print "Config ended.\n";
205
206 print $sock "FILTER\n";
207 $response = <$sock>;
208 if (!$response) {
209 print "Failed: Could not get a filter address from the filter manager.\n";
210 close($sock);
211 wait_then_retry();
212 next;
213 }
214 chop $response;
215 $response =~ /^(.*);(.*);(.*)/;
216 if ($response eq "ERROR") {
217 print "There are no active configured filters for your host.\n";
218 close($sock);
219 wait_then_retry();
220 next;
221 }
222 ($filter_addr, $udp_port, $tcp_port) = ($1, $2, $3);
223 unless (defined($filter_addr) && defined($udp_port) && defined($tcp_port)) {
224 print "Failed: Filter address response from server did not make sense: $response\n";
225 close($sock);
226 wait_then_retry();
227 next;
228 }
229
230 print "Got filter data ($filter_addr, $udp_port, $tcp_port)\n";
231
232 print $sock "END\n";
233 $response = <$sock>;
234 if ($response && ($response eq "OK\n")) {
235 print "Host successfully configured via TCP.\n"
236 }
237 else {
238 print "The server failed the host configuration on the END command.\n";
239 close($sock);
240 wait_then_retry();
241 next;
242 }
243
244 close($sock);
245
246 print "Configuration finished sucessfully!\n";
247 last;
248 }
249 return;
250 }
251
252
253
254
255 #-----------------------------------------------------------------------
256 # send_udp_packet
257 # Sends a UDP packet to an i-scream filter.
258 # The packet contains XML markup describing some of the machine's state.
259 # Receipt of UDP packets is not guaranteed.
260 #-----------------------------------------------------------------------
261 sub send_udp_packet() {
262
263 my(@statgrab) = `./statgrab.pl`;
264 my(%packet);
265 for (my($i) = 0; $i <= $#statgrab; $i++) {
266 $statgrab[$i] =~ /^([^\s]*) (.*)$/;
267 $packet{$1} = $2;
268 }
269
270 my($date) = time;
271
272 my($disk_info) = "<disk>";
273 my($i) = 0;
274 while (defined $packet{"packet.disk.p$i.attributes.mount"}) {
275 $disk_info .= "<p$i";
276 $disk_info .= " name=\"" . $packet{"packet.disk.p$i.attributes.name"} . "\"";
277 $disk_info .= " kbytes=\"" . $packet{"packet.disk.p$i.attributes.kbytes"} . "\"";
278 $disk_info .= " used=\"" . $packet{"packet.disk.p$i.attributes.used"} . "\"";
279 $disk_info .= " avail=\"" . $packet{"packet.disk.p$i.attributes.avail"} . "\"";
280 $disk_info .= " mount=\"" . $packet{"packet.disk.p$i.attributes.mount"} . "\"";
281 $disk_info .= "></p$i>";
282 ++$i;
283 }
284 $disk_info .= "</disk>";
285
286 my($ip) = inet_ntoa(scalar(gethostbyname(hostname())) || 'localhost');
287
288 # Build the XML packet this way, as we can clearly
289 # see the structure and contents... I like this ;-)
290 # [Note that the server rejects UDP packets that are
291 # larger than 8196 bytes]
292 my($xml) = <<EOF;
293
294 <packet seq_no="$seq_no" machine_name="$fqdn" date="$date" type="data" ip="$ip">
295 <load>
296 <load1>$packet{"packet.load.load1"}</load1>
297 <load5>$packet{"packet.load.load5"}</load5>
298 <load15>$packet{"packet.load.load15"}</load15>
299 </load>
300 <os>
301 <name>$packet{"packet.os.name"}</name>
302 <release>$packet{"packet.os.release"}</release>
303 <platform>$packet{"packet.os.platform"}</platform>
304 <sysname>$packet{"packet.os.sysname"}</sysname>
305 <version>$packet{"packet.os.version"}</version>
306 <uptime>$packet{"packet.os.uptime"}</uptime>
307 </os>
308 <users>
309 <count>$packet{"packet.users.count"}</count>
310 <list>$packet{"packet.users.list"}</list>
311 </users>
312 <processes>
313 <total>$packet{"packet.processes.total"}</total>
314 <sleeping>$packet{"packet.processes.sleeping"}</sleeping>
315 <zombie>$packet{"packet.processes.zombie"}</zombie>
316 <stopped>$packet{"packet.processes.stopped"}</stopped>
317 <cpu>$packet{"packet.processes.cpu"}</cpu>
318 </processes>
319 <cpu>
320 <idle>$packet{"packet.cpu.idle"}</idle>
321 <user>$packet{"packet.cpu.user"}</user>
322 <kernel>$packet{"packet.cpu.kernel"}</kernel>
323 <iowait>$packet{"packet.cpu.iowait"}</iowait>
324 <swap>$packet{"packet.cpu.swap"}</swap>
325 </cpu>
326 <memory>
327 <total>$packet{"packet.memory.total"}</total>
328 <free>$packet{"packet.memory.free"}</free>
329 </memory>
330 <swap>
331 <total>$packet{"packet.swap.total"}</total>
332 <free>$packet{"packet.swap.free"}</free>
333 </swap>
334 $disk_info
335 </packet>
336
337 EOF
338
339 # Make the packet smaller by stripping out newlines and leading spaces.
340 $xml =~ s/\n\s*//g;
341
342 my($sock) = new IO::Socket::INET (
343 PeerPort => $udp_port,
344 PeerAddr => $filter_addr,
345 Proto => 'udp'
346 ) or die "Could not send UDP: $!\n";
347
348 print $sock $xml or die "Could not send UDP packet: $!\n";
349 close($sock);
350 $seq_no++;
351 print "-";
352
353 return;
354 }
355
356
357
358
359 #-----------------------------------------------------------------------
360 # send_tcp_heartbeat
361 # Establishes a TCP connection to an i-scream filter.
362 # The heartbeat is used as a guaranteed "I'm alive" delivery mechanism.
363 #-----------------------------------------------------------------------
364 sub send_tcp_heartbeat() {
365
366 my($sock) = new IO::Socket::INET(
367 PeerAddr => $filter_addr,
368 PeerPort => $tcp_port,
369 Proto => 'tcp'
370 ) or return;
371 if (!defined $sock) {
372 print "IHOST WARNING: Failed to deliver a heartbeat to the i-scream filter.\n";
373 &tcp_configure();
374 return;
375 }
376
377 # Now run through the configuration process.
378 my($response);
379
380 print $sock "HEARTBEAT\n";
381 $response = <$sock>;
382 if (!$response eq "OK\n") {
383 close($sock);
384 print "Server gave wrong response to HEARTBEAT: $response\n";
385 &tcp_configure();
386 return;
387 }
388
389 print $sock "CONFIG\n";
390 $response = <$sock>;
391 if (!$response eq "OK\n") {
392 close($sock);
393 print "Server gave wrong response to CONFIG: $response\n";
394 &tcp_configure();
395 return;
396 }
397
398 print $sock "$file_list\n";
399 $response = <$sock>;
400 if (!$response eq "OK\n") {
401 close($sock);
402 print "Server gave wrong response to file list: $response\n";
403 &tcp_configure();
404 return;
405 }
406
407 print $sock "$last_modified\n";
408 $response = <$sock>;
409 if ($response eq "ERROR\n") {
410 close($sock);
411 print "Server configuration changed. Reconfiguring with filter manager.\n";
412 &tcp_configure();
413 return;
414 }
415 if (!$response eq "OK\n") {
416 close($sock);
417 print "Server gave wrong response to HEARTBEAT: $response\n";
418 &tcp_configure();
419 return;
420 }
421
422 print $sock "ENDHEARTBEAT\n";
423 $response = <$sock>;
424 if (!$response eq "OK\n") {
425 close($sock);
426 print "Server gave wrong response to ENDHEARTBEAT: $response\n";
427 &tcp_configure();
428 return;
429 }
430
431 close($sock);
432 print "^";
433
434 return;
435 }
436
437 #-----------------------------------------------------------------------
438 # write_pid
439 # Writes the PID (process ID) of this instance to $pidfile.
440 # This is then used by a seperate script to check (and restart) ihost.
441 #-----------------------------------------------------------------------
442 sub write_pid() {
443 open PID, ">$pidfile";
444 print PID $$;
445 close PID;
446
447 return;
448 }