node_status.in 2.47 KB
Newer Older
1 2
#!/usr/bin/perl -w

3 4 5 6 7 8 9
#
# node_status - Updates the 'status' column in the nodes table to indicate
# whether nodes are pingable, etc.
# Intended to be run as a cronjob
# Requires 'fping' to be installed
#

10 11 12 13
############################## Defines and includes
my $fping = "/usr/local/sbin/fping"; # Path to fping

use strict;
14
use English;
15 16
use IPC::Open2;

17 18 19 20 21 22 23 24 25 26
# Configure variables
use lib '@prefix@/lib';
use libdb;

#
# Only root and admins are allowed to use this script
#
if (($UID != 0) && (!TBAdmin())) {
	die "Only root and admins are allowed to use this script\n";
}
27 28 29 30

############################## Get node list
my $query = "SELECT nodes.node_id, nodes.status, os_info.osfeatures " .
            "FROM nodes LEFT JOIN os_info ON nodes.def_boot_osid = os_info.osid ";
31
my $result = DBQueryFatal($query);
32 33 34 35

############################## Determine pingable/unpingable nodes
my (@newlyUp, @newlyDown, @newlyPD, @newlyUnpingable) = ();

36
my $fpingPID = &open2("FOUT","FIN","$fping 2>&1"); # Throws an exception on failure
37 38 39

my %oldStatus = (); # Status of node from the DB, so we can tell if it changed

40
while (my %row = $result->fetchhash) {
41
	my $node = $row{node_id};
42
	$oldStatus{$node} = $row{status};
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
	if ($row{osfeatures} && ($row{osfeatures} =~ /ping/)) {
		# We have a node that should be capable of returning pings
		print FIN $node,"\n"; # Give fping another node to ping
	} else {
		# This node can't return pings
		if ((!$row{status}) || ($row{status} ne 'unpingable')) {
			push @newlyUnpingable, $node;
		}
	}
}
close(FIN); # Tell fping we're done giving it nodes

############################## Determine status changes
while (<FOUT>) { # Read fping results
	chomp;
	my ($node,$status) = split /\s+/,$_,2;
59 60
	# Skip ICMP messages
	next if ($node eq "ICMP");
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
	
	if ($status eq "is alive") {
		if ($oldStatus{$node} ne "up") {
			push @newlyUp,$node;
		}
	} else { # Node must not have returned a ping
		if ($oldStatus{$node} eq "possibly down") {
			push @newlyDown, $node;
		} elsif ($oldStatus{$node} ne "down") {
			push @newlyPD, $node;
		}
	}
}
close(FOUT);

############################## Write back changes
# When I started this section, it seemed a clever way to avoid code
# duplication. Now, I'm not so sure :)
foreach (['down', @newlyDown], ['up', @newlyUp],
		['possibly down', @newlyPD], ['unpingable', @newlyUnpingable]) {
	my $status = shift @$_;
	my @nodes = @$_;
	if (@nodes) {
		my $query = "UPDATE nodes SET status='$status' WHERE " .
			join " OR ", map("node_id='$_'",@nodes);
86
		DBQueryFatal($query);
87 88
	}
}