Micke Nordin 8 years ago
commit 24bc37f72d

@ -1,2 +1,23 @@
# cluster_cron
A perlscript that makes it possible to cluster cron via a shared drive
In order to use this you need a shared drive on all servers that is going to be part of the cluster such as afs, glusterfs, nfs or samba.
Install this script somwhere and make sure it is executable. In this example the script will be put in:
/usr/local/bin/cluster_cron.pl
Next you need to add the script to the crontab of some user which will not be synced on both servers, for example the root user.
The script takes 4 arguments of which two are optional:
user = the user that gets the cron file synced, in this example we are using a user called cluster
shared directory = the shared drive you set up previously, in this example the path is /mnt/shareddir
mode = either 0 for active/passive or 1 for active/active if this argument is not supplied 1 is assumed
cron spool directory = defaults to /var/spool/cron/crontabs
So as root:
crontab -e
add this line:
```
* * * * * /usr/local/bin/cluster_cron.pl cluster /mnt/shareddir 0 /var/spool/cron/crontabs >> /mnt/shareddir/cluster.log 2>&1
```

@ -1,188 +0,0 @@
#!/usr/bin/env perl
use warnings;
use strict;
use bignum;
use Sys::Hostname;
use File::Compare;
use File::Copy;
use File::Temp;
my $num_args = $#ARGV + 1;
if ($num_args < 2) {
print "\nUsage: $0 <user> <shared directory> [cron spool directory]\n";
exit;
}
# We need a common directory for our nodes to write and read state from plus a couple of cronfiles
my ($user, $shareddir, $spooldir) = @ARGV;
# Set some defaults if we didn't get them
unless ($spooldir) {
$spooldir = "/var/spool/cron/crontabs";
}
my $sharedcrondir = "$shareddir/crontab";
my $cronfile = "$spooldir/$user";
my $activesharedcronfile = "$sharedcrondir/$user";
my $oldactivesharedcronfile = "$sharedcrondir/$user.old";
my $electiondir = "$sharedcrondir/election";
my $hostname = hostname;
my $failovertimeout = 40;
# Create directories
unless (-d $sharedcrondir) {
mkdir $sharedcrondir or die "Can not create shared directory: $!";
}
# Get the nodes in the cluster
sub get_nodes {
# Our data type is a hash with the following fields
# name, time, numeric
my @nodes = ();
my @files = <$electiondir/*>;
foreach my $filename (@files) {
chomp $filename;
my $nodename = $filename;
$nodename =~ s/^$electiondir\///;
# Don't add this host
unless ($nodename eq $hostname) {
my $node = {};
open(FILE, "<$filename") or die "Could not open file: $!";
$node->{'name'} = $nodename;
my $content = <FILE>;
chomp $content;
$node->{'time'} = $content;
$node->{'numeric'} = get_numeric($nodename);
close FILE;
push @nodes, $node;
}
}
return @nodes;
}
# Simply write a unix timestamp to a file with our name on it
sub write_timestamp {
my $time = time;
open(FILE,"+>$electiondir/$hostname");
print FILE $time;
close FILE;
}
# Lets see if we are the active node
sub is_active {
my @nodes = @_;
# Assume you are active
my $state = 1;
# Loop all nodes
foreach my $node (@nodes) {
# If it has been active in the last 120 seconds it is eligable
my $diff = time - $node->{'time'};
if( $diff < $failovertimeout ) {
print "Node: $node->{'name'} was active last $failovertimeout sec\n";
# Go to passive mode if the numeric of that host is less than that of this host
my $this = get_numeric($hostname);
my $that = $node->{'numeric'};
print "Numeric for $hostname is $this and for $node->{'name'} is $that\n";
if( $this > $that ) {
$state = 0 ;
print "I am not active\n";
} else {
print "I am active\n";
my $outfile = "/var/lib/thruk/crontab/active";
open(OUTFILE,"+>$outfile");
print OUTFILE $hostname;
close OUTFILE;
}
} else {
print "Node: $node->{'name'} was not active last 120 sec\n";
}
}
return $state;
}
# Turn a hostname in to a decimal number used for comparison, lowest numer gets to be active if it is working
sub get_numeric {
my ($host) = @_;
my @ascii = unpack 'C*', $host;
my $numeric = '';
foreach my $val (@ascii) {
$numeric .= $val;
}
return $numeric + 0;
}
sub cron_compare {
my ($one, $two, $three) = @_;
# Current cronfile and shared cronfile is not same
if(compare($one, $two) != 0) {
print "$one and $two is not the same\n";
# This means that the other node has changed the cron file
if(compare($one, $three) == 0) {
print "Some other node has changed the file\n";
return 1;
# This means that my node has changed the cron file
} else {
print "This node has changed the cron file\n";
return 2;
}
}
return 0;
}
sub run {
print "Host: $hostname started run at: " . time . "\n";
print "User: $user, shareddir: $shareddir and spooldir: $spooldir\n";
if(-e $activesharedcronfile) {
unless(-e $oldactivesharedcronfile) {
copy($activesharedcronfile, $oldactivesharedcronfile);
}
}
unless (-d $electiondir) {
mkdir $electiondir;
}
# Update timestamp
write_timestamp;
is_active(get_nodes);
my $compare = cron_compare($cronfile, $activesharedcronfile, $oldactivesharedcronfile );
# The other node has changed the file
if($compare == 1) {
copy($activesharedcronfile, $cronfile);
copy($activesharedcronfile, $oldactivesharedcronfile);
# We have changed things
} elsif ($compare == 2) {
copy($cronfile, $activesharedcronfile);
}
print "Host: $hostname finished run at: " . time . "\n\n\n";
}
for (my $i = 0; $i < 6; $i++) {
my $start = time;
$| = 1;
run;
if ((my $remaining = 10 - (time - $start)) > 0) {
sleep $remaining;
}
}
exit;
Loading…
Cancel
Save