Merge branch 'master' of https://github.com/mickenordin/cluster_cron
commit
24bc37f72d
@ -1,2 +1,23 @@
|
|||||||
# cluster_cron
|
# cluster_cron
|
||||||
A perlscript that makes it possible to cluster cron via a shared drive
|
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…
Reference in new issue